| author | |
| committer | |
| log | efdc94c10712f610e7de5e49fd9cd6f88b4bbbae |
| tree | 4b66ec30176843b0efd87b73199c75aa2fba675d |
| parent | 06df842e4d313e81444063803deff306602e0a17 |
| parent | c32171991b25b323cd68ff96c294bf5a6fa753b8 |
121 files changed, 6904 insertions(+), 4184 deletions(-)
CMakeLists.txt+3-2| ... | @@ -216,6 +216,9 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -216,6 +216,9 @@ set(ZIG_STAGE2_SOURCES |
| 216 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/stack.zig" | 216 | "${CMAKE_SOURCE_DIR}/lib/std/atomic/stack.zig" |
| 217 | "${CMAKE_SOURCE_DIR}/lib/std/base64.zig" | 217 | "${CMAKE_SOURCE_DIR}/lib/std/base64.zig" |
| 218 | "${CMAKE_SOURCE_DIR}/lib/std/buf_map.zig" | 218 | "${CMAKE_SOURCE_DIR}/lib/std/buf_map.zig" |
| 219 | "${CMAKE_SOURCE_DIR}/lib/std/Build.zig" | ||
| 220 | "${CMAKE_SOURCE_DIR}/lib/std/Build/Cache.zig" | ||
| 221 | "${CMAKE_SOURCE_DIR}/lib/std/Build/Cache/DepTokenizer.zig" | ||
| 219 | "${CMAKE_SOURCE_DIR}/lib/std/builtin.zig" | 222 | "${CMAKE_SOURCE_DIR}/lib/std/builtin.zig" |
| 220 | "${CMAKE_SOURCE_DIR}/lib/std/c.zig" | 223 | "${CMAKE_SOURCE_DIR}/lib/std/c.zig" |
| 221 | "${CMAKE_SOURCE_DIR}/lib/std/c/linux.zig" | 224 | "${CMAKE_SOURCE_DIR}/lib/std/c/linux.zig" |
| ... | @@ -523,9 +526,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -523,9 +526,7 @@ set(ZIG_STAGE2_SOURCES |
| 523 | "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig" | 526 | "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig" |
| 524 | "${CMAKE_SOURCE_DIR}/src/Air.zig" | 527 | "${CMAKE_SOURCE_DIR}/src/Air.zig" |
| 525 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" | 528 | "${CMAKE_SOURCE_DIR}/src/AstGen.zig" |
| 526 | "${CMAKE_SOURCE_DIR}/src/Cache.zig" | ||
| 527 | "${CMAKE_SOURCE_DIR}/src/Compilation.zig" | 529 | "${CMAKE_SOURCE_DIR}/src/Compilation.zig" |
| 528 | "${CMAKE_SOURCE_DIR}/src/DepTokenizer.zig" | ||
| 529 | "${CMAKE_SOURCE_DIR}/src/Liveness.zig" | 530 | "${CMAKE_SOURCE_DIR}/src/Liveness.zig" |
| 530 | "${CMAKE_SOURCE_DIR}/src/Module.zig" | 531 | "${CMAKE_SOURCE_DIR}/src/Module.zig" |
| 531 | "${CMAKE_SOURCE_DIR}/src/Package.zig" | 532 | "${CMAKE_SOURCE_DIR}/src/Package.zig" |
build.zig+5-7| ... | @@ -40,15 +40,11 @@ pub fn build(b: *std.Build) !void { | ... | @@ -40,15 +40,11 @@ pub fn build(b: *std.Build) !void { |
| 40 | }); | 40 | }); |
| 41 | docgen_exe.single_threaded = single_threaded; | 41 | docgen_exe.single_threaded = single_threaded; |
| 42 | 42 | ||
| 43 | const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe); | 43 | const langref_out_path = try b.cache_root.join(b.allocator, &.{"langref.html"}); |
| 44 | const langref_out_path = fs.path.join( | ||
| 45 | b.allocator, | ||
| 46 | &[_][]const u8{ b.cache_root, "langref.html" }, | ||
| 47 | ) catch unreachable; | ||
| 48 | const docgen_cmd = docgen_exe.run(); | 44 | const docgen_cmd = docgen_exe.run(); |
| 49 | docgen_cmd.addArgs(&[_][]const u8{ | 45 | docgen_cmd.addArgs(&[_][]const u8{ |
| 50 | "--zig", | 46 | "--zig", |
| 51 | rel_zig_exe, | 47 | b.zig_exe, |
| 52 | "doc" ++ fs.path.sep_str ++ "langref.html.in", | 48 | "doc" ++ fs.path.sep_str ++ "langref.html.in", |
| 53 | langref_out_path, | 49 | langref_out_path, |
| 54 | }); | 50 | }); |
| ... | @@ -133,6 +129,8 @@ pub fn build(b: *std.Build) !void { | ... | @@ -133,6 +129,8 @@ pub fn build(b: *std.Build) !void { |
| 133 | "compress-gettysburg.txt", | 129 | "compress-gettysburg.txt", |
| 134 | "compress-pi.txt", | 130 | "compress-pi.txt", |
| 135 | "rfc1951.txt", | 131 | "rfc1951.txt", |
| 132 | // exclude files from lib/std/compress/lzma/testdata | ||
| 133 | ".lzma", | ||
| 136 | // exclude files from lib/std/compress/xz/testdata | 134 | // exclude files from lib/std/compress/xz/testdata |
| 137 | ".xz", | 135 | ".xz", |
| 138 | // exclude files from lib/std/tz/ | 136 | // exclude files from lib/std/tz/ |
| ... | @@ -219,7 +217,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -219,7 +217,7 @@ pub fn build(b: *std.Build) !void { |
| 219 | 217 | ||
| 220 | var code: u8 = undefined; | 218 | var code: u8 = undefined; |
| 221 | const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{ | 219 | const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{ |
| 222 | "git", "-C", b.build_root, "describe", "--match", "*.*.*", "--tags", | 220 | "git", "-C", b.build_root.path orelse ".", "describe", "--match", "*.*.*", "--tags", |
| 223 | }, &code, .Ignore) catch { | 221 | }, &code, .Ignore) catch { |
| 224 | break :v version_string; | 222 | break :v version_string; |
| 225 | }; | 223 | }; |
doc/langref.html.in+79-70| ... | @@ -5393,7 +5393,6 @@ pub fn parseU64(buf: []const u8, radix: u8) !u64 { | ... | @@ -5393,7 +5393,6 @@ pub fn parseU64(buf: []const u8, radix: u8) !u64 { |
| 5393 | // x *= radix | 5393 | // x *= radix |
| 5394 | var ov = @mulWithOverflow(x, radix); | 5394 | var ov = @mulWithOverflow(x, radix); |
| 5395 | if (ov[1] != 0) return error.OverFlow; | 5395 | if (ov[1] != 0) return error.OverFlow; |
| 5396 | |||
| 5397 | 5396 | ||
| 5398 | // x += digit | 5397 | // x += digit |
| 5399 | ov = @addWithOverflow(ov[0], digit); | 5398 | ov = @addWithOverflow(ov[0], digit); |
| ... | @@ -6067,7 +6066,7 @@ struct Foo *do_a_thing(void) { | ... | @@ -6067,7 +6066,7 @@ struct Foo *do_a_thing(void) { |
| 6067 | <p>Zig code</p> | 6066 | <p>Zig code</p> |
| 6068 | {#syntax_block|zig|call_malloc_from_zig.zig#} | 6067 | {#syntax_block|zig|call_malloc_from_zig.zig#} |
| 6069 | // malloc prototype included for reference | 6068 | // malloc prototype included for reference |
| 6070 | extern fn malloc(size: size_t) ?*u8; | 6069 | extern fn malloc(size: usize) ?*u8; |
| 6071 | 6070 | ||
| 6072 | fn doAThing() ?*Foo { | 6071 | fn doAThing() ?*Foo { |
| 6073 | const ptr = malloc(1234) orelse return null; | 6072 | const ptr = malloc(1234) orelse return null; |
| ... | @@ -7479,64 +7478,66 @@ pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { | ... | @@ -7479,64 +7478,66 @@ pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { |
| 7479 | <p> | 7478 | <p> |
| 7480 | Dissecting the syntax: | 7479 | Dissecting the syntax: |
| 7481 | </p> | 7480 | </p> |
| 7482 | {#syntax_block|zig|Assembly Syntax Explained#} | 7481 | {#code_begin|syntax|Assembly Syntax Explained#} |
| 7483 | // Inline assembly is an expression which returns a value. | 7482 | pub fn syscall1(number: usize, arg1: usize) usize { |
| 7484 | // the `asm` keyword begins the expression. | 7483 | // Inline assembly is an expression which returns a value. |
| 7485 | _ = asm | 7484 | // the `asm` keyword begins the expression. |
| 7486 | // `volatile` is an optional modifier that tells Zig this | 7485 | return asm |
| 7487 | // inline assembly expression has side-effects. Without | 7486 | // `volatile` is an optional modifier that tells Zig this |
| 7488 | // `volatile`, Zig is allowed to delete the inline assembly | 7487 | // inline assembly expression has side-effects. Without |
| 7489 | // code if the result is unused. | 7488 | // `volatile`, Zig is allowed to delete the inline assembly |
| 7490 | volatile ( | 7489 | // code if the result is unused. |
| 7491 | // Next is a comptime string which is the assembly code. | 7490 | volatile ( |
| 7492 | // Inside this string one may use `%[ret]`, `%[number]`, | 7491 | // Next is a comptime string which is the assembly code. |
| 7493 | // or `%[arg1]` where a register is expected, to specify | 7492 | // Inside this string one may use `%[ret]`, `%[number]`, |
| 7494 | // the register that Zig uses for the argument or return value, | 7493 | // or `%[arg1]` where a register is expected, to specify |
| 7495 | // if the register constraint strings are used. However in | 7494 | // the register that Zig uses for the argument or return value, |
| 7496 | // the below code, this is not used. A literal `%` can be | 7495 | // if the register constraint strings are used. However in |
| 7497 | // obtained by escaping it with a double percent: `%%`. | 7496 | // the below code, this is not used. A literal `%` can be |
| 7498 | // Often multiline string syntax comes in handy here. | 7497 | // obtained by escaping it with a double percent: `%%`. |
| 7498 | // Often multiline string syntax comes in handy here. | ||
| 7499 | \\syscall | 7499 | \\syscall |
| 7500 | // Next is the output. It is possible in the future Zig will | 7500 | // Next is the output. It is possible in the future Zig will |
| 7501 | // support multiple outputs, depending on how | 7501 | // support multiple outputs, depending on how |
| 7502 | // https://github.com/ziglang/zig/issues/215 is resolved. | 7502 | // https://github.com/ziglang/zig/issues/215 is resolved. |
| 7503 | // It is allowed for there to be no outputs, in which case | 7503 | // It is allowed for there to be no outputs, in which case |
| 7504 | // this colon would be directly followed by the colon for the inputs. | 7504 | // this colon would be directly followed by the colon for the inputs. |
| 7505 | : | 7505 | : |
| 7506 | // This specifies the name to be used in `%[ret]` syntax in | 7506 | // This specifies the name to be used in `%[ret]` syntax in |
| 7507 | // the above assembly string. This example does not use it, | 7507 | // the above assembly string. This example does not use it, |
| 7508 | // but the syntax is mandatory. | 7508 | // but the syntax is mandatory. |
| 7509 | [ret] | 7509 | [ret] |
| 7510 | // Next is the output constraint string. This feature is still | 7510 | // Next is the output constraint string. This feature is still |
| 7511 | // considered unstable in Zig, and so LLVM/GCC documentation | 7511 | // considered unstable in Zig, and so LLVM/GCC documentation |
| 7512 | // must be used to understand the semantics. | 7512 | // must be used to understand the semantics. |
| 7513 | // http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string | 7513 | // http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string |
| 7514 | // https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html | 7514 | // https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html |
| 7515 | // In this example, the constraint string means "the result value of | 7515 | // In this example, the constraint string means "the result value of |
| 7516 | // this inline assembly instruction is whatever is in $rax". | 7516 | // this inline assembly instruction is whatever is in $rax". |
| 7517 | "={rax}" | 7517 | "={rax}" |
| 7518 | // Next is either a value binding, or `->` and then a type. The | 7518 | // Next is either a value binding, or `->` and then a type. The |
| 7519 | // type is the result type of the inline assembly expression. | 7519 | // type is the result type of the inline assembly expression. |
| 7520 | // If it is a value binding, then `%[ret]` syntax would be used | 7520 | // If it is a value binding, then `%[ret]` syntax would be used |
| 7521 | // to refer to the register bound to the value. | 7521 | // to refer to the register bound to the value. |
| 7522 | (-> usize) | 7522 | (-> usize) |
| 7523 | // Next is the list of inputs. | 7523 | // Next is the list of inputs. |
| 7524 | // The constraint for these inputs means, "when the assembly code is | 7524 | // The constraint for these inputs means, "when the assembly code is |
| 7525 | // executed, $rax shall have the value of `number` and $rdi shall have | 7525 | // executed, $rax shall have the value of `number` and $rdi shall have |
| 7526 | // the value of `arg1`". Any number of input parameters is allowed, | 7526 | // the value of `arg1`". Any number of input parameters is allowed, |
| 7527 | // including none. | 7527 | // including none. |
| 7528 | : [number] "{rax}" (number), | 7528 | : [number] "{rax}" (number), |
| 7529 | [arg1] "{rdi}" (arg1) | 7529 | [arg1] "{rdi}" (arg1) |
| 7530 | // Next is the list of clobbers. These declare a set of registers whose | 7530 | // Next is the list of clobbers. These declare a set of registers whose |
| 7531 | // values will not be preserved by the execution of this assembly code. | 7531 | // values will not be preserved by the execution of this assembly code. |
| 7532 | // These do not include output or input registers. The special clobber | 7532 | // These do not include output or input registers. The special clobber |
| 7533 | // value of "memory" means that the assembly writes to arbitrary undeclared | 7533 | // value of "memory" means that the assembly writes to arbitrary undeclared |
| 7534 | // memory locations - not only the memory pointed to by a declared indirect | 7534 | // memory locations - not only the memory pointed to by a declared indirect |
| 7535 | // output. In this example we list $rcx and $r11 because it is known the | 7535 | // output. In this example we list $rcx and $r11 because it is known the |
| 7536 | // kernel syscall does not preserve these registers. | 7536 | // kernel syscall does not preserve these registers. |
| 7537 | : "rcx", "r11" | 7537 | : "rcx", "r11" |
| 7538 | ); | 7538 | ); |
| 7539 | {#end_syntax_block#} | 7539 | } |
| 7540 | {#code_end#} | ||
| 7540 | <p> | 7541 | <p> |
| 7541 | For x86 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more | 7542 | For x86 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more |
| 7542 | popular Intel syntax. This is due to technical constraints; assembly parsing is | 7543 | popular Intel syntax. This is due to technical constraints; assembly parsing is |
| ... | @@ -7892,7 +7893,7 @@ fn add(a: i32, b: i32) i32 { | ... | @@ -7892,7 +7893,7 @@ fn add(a: i32, b: i32) i32 { |
| 7892 | {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The | 7893 | {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The |
| 7893 | {#syntax#}CallModifier{#endsyntax#} enum is reproduced here: | 7894 | {#syntax#}CallModifier{#endsyntax#} enum is reproduced here: |
| 7894 | </p> | 7895 | </p> |
| 7895 | {#syntax_block|zig|builtin.CallModifier struct#} | 7896 | {#code_begin|syntax|builtin.CallModifier struct#} |
| 7896 | pub const CallModifier = enum { | 7897 | pub const CallModifier = enum { |
| 7897 | /// Equivalent to function call syntax. | 7898 | /// Equivalent to function call syntax. |
| 7898 | auto, | 7899 | auto, |
| ... | @@ -7926,7 +7927,7 @@ pub const CallModifier = enum { | ... | @@ -7926,7 +7927,7 @@ pub const CallModifier = enum { |
| 7926 | /// compile-time, a compile error is emitted instead. | 7927 | /// compile-time, a compile error is emitted instead. |
| 7927 | compile_time, | 7928 | compile_time, |
| 7928 | }; | 7929 | }; |
| 7929 | {#end_syntax_block#} | 7930 | {#code_end#} |
| 7930 | {#header_close#} | 7931 | {#header_close#} |
| 7931 | 7932 | ||
| 7932 | {#header_open|@cDefine#} | 7933 | {#header_open|@cDefine#} |
| ... | @@ -8128,6 +8129,13 @@ test "main" { | ... | @@ -8128,6 +8129,13 @@ test "main" { |
| 8128 | {#code_end#} | 8129 | {#code_end#} |
| 8129 | {#header_close#} | 8130 | {#header_close#} |
| 8130 | 8131 | ||
| 8132 | {#header_open|@constCast#} | ||
| 8133 | <pre>{#syntax#}@constCast(value: anytype) DestType{#endsyntax#}</pre> | ||
| 8134 | <p> | ||
| 8135 | Remove {#syntax#}const{#endsyntax#} qualifier from a pointer. | ||
| 8136 | </p> | ||
| 8137 | {#header_close#} | ||
| 8138 | |||
| 8131 | {#header_open|@ctz#} | 8139 | {#header_open|@ctz#} |
| 8132 | <pre>{#syntax#}@ctz(operand: anytype){#endsyntax#}</pre> | 8140 | <pre>{#syntax#}@ctz(operand: anytype){#endsyntax#}</pre> |
| 8133 | <p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.</p> | 8141 | <p>{#syntax#}@TypeOf(operand){#endsyntax#} must be an integer type or an integer vector type.</p> |
| ... | @@ -8813,7 +8821,8 @@ pub const PrefetchOptions = struct { | ... | @@ -8813,7 +8821,8 @@ pub const PrefetchOptions = struct { |
| 8813 | {#syntax#}@ptrCast{#endsyntax#} cannot be used for: | 8821 | {#syntax#}@ptrCast{#endsyntax#} cannot be used for: |
| 8814 | </p> | 8822 | </p> |
| 8815 | <ul> | 8823 | <ul> |
| 8816 | <li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@qualCast#}.</li> | 8824 | <li>Removing {#syntax#}const{#endsyntax#} qualifier, use {#link|@constCast#}.</li> |
| 8825 | <li>Removing {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@volatileCast#}.</li> | ||
| 8817 | <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li> | 8826 | <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li> |
| 8818 | <li>Increasing pointer alignment, use {#link|@alignCast#}.</li> | 8827 | <li>Increasing pointer alignment, use {#link|@alignCast#}.</li> |
| 8819 | <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li> | 8828 | <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li> |
| ... | @@ -8830,13 +8839,6 @@ pub const PrefetchOptions = struct { | ... | @@ -8830,13 +8839,6 @@ pub const PrefetchOptions = struct { |
| 8830 | 8839 | ||
| 8831 | {#header_close#} | 8840 | {#header_close#} |
| 8832 | 8841 | ||
| 8833 | {#header_open|@qualCast#} | ||
| 8834 | <pre>{#syntax#}@qualCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre> | ||
| 8835 | <p> | ||
| 8836 | Remove {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier from a pointer. | ||
| 8837 | </p> | ||
| 8838 | {#header_close#} | ||
| 8839 | |||
| 8840 | {#header_open|@rem#} | 8842 | {#header_open|@rem#} |
| 8841 | <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre> | 8843 | <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| 8842 | <p> | 8844 | <p> |
| ... | @@ -9524,6 +9526,13 @@ fn foo(comptime T: type, ptr: *T) T { | ... | @@ -9524,6 +9526,13 @@ fn foo(comptime T: type, ptr: *T) T { |
| 9524 | <pre>{#syntax#}@Vector(len: comptime_int, Element: type) type{#endsyntax#}</pre> | 9526 | <pre>{#syntax#}@Vector(len: comptime_int, Element: type) type{#endsyntax#}</pre> |
| 9525 | <p>Creates {#link|Vectors#}.</p> | 9527 | <p>Creates {#link|Vectors#}.</p> |
| 9526 | {#header_close#} | 9528 | {#header_close#} |
| 9529 | |||
| 9530 | {#header_open|@volatileCast#} | ||
| 9531 | <pre>{#syntax#}@volatileCast(value: anytype) DestType{#endsyntax#}</pre> | ||
| 9532 | <p> | ||
| 9533 | Remove {#syntax#}volatile{#endsyntax#} qualifier from a pointer. | ||
| 9534 | </p> | ||
| 9535 | {#header_close#} | ||
| 9527 | {#header_close#} | 9536 | {#header_close#} |
| 9528 | 9537 | ||
| 9529 | {#header_open|Build Mode#} | 9538 | {#header_open|Build Mode#} |
| ... | @@ -9604,7 +9613,7 @@ pub fn build(b: *std.Build) void { | ... | @@ -9604,7 +9613,7 @@ pub fn build(b: *std.Build) void { |
| 9604 | {#header_close#} | 9613 | {#header_close#} |
| 9605 | 9614 | ||
| 9606 | {#header_open|Single Threaded Builds#} | 9615 | {#header_open|Single Threaded Builds#} |
| 9607 | <p>Zig has a compile option <kbd>--single-threaded</kbd> which has the following effects:</p> | 9616 | <p>Zig has a compile option <kbd>-fsingle-threaded</kbd> which has the following effects:</p> |
| 9608 | <ul> | 9617 | <ul> |
| 9609 | <li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li> | 9618 | <li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li> |
| 9610 | <li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li> | 9619 | <li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li> |
lib/build_runner.zig+53-12| ... | @@ -43,13 +43,40 @@ pub fn main() !void { | ... | @@ -43,13 +43,40 @@ pub fn main() !void { |
| 43 | 43 | ||
| 44 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); | 44 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); |
| 45 | 45 | ||
| 46 | const build_root_directory: std.Build.Cache.Directory = .{ | ||
| 47 | .path = build_root, | ||
| 48 | .handle = try std.fs.cwd().openDir(build_root, .{}), | ||
| 49 | }; | ||
| 50 | |||
| 51 | const local_cache_directory: std.Build.Cache.Directory = .{ | ||
| 52 | .path = cache_root, | ||
| 53 | .handle = try std.fs.cwd().makeOpenPath(cache_root, .{}), | ||
| 54 | }; | ||
| 55 | |||
| 56 | const global_cache_directory: std.Build.Cache.Directory = .{ | ||
| 57 | .path = global_cache_root, | ||
| 58 | .handle = try std.fs.cwd().makeOpenPath(global_cache_root, .{}), | ||
| 59 | }; | ||
| 60 | |||
| 61 | var cache: std.Build.Cache = .{ | ||
| 62 | .gpa = allocator, | ||
| 63 | .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), | ||
| 64 | }; | ||
| 65 | cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() }); | ||
| 66 | cache.addPrefix(build_root_directory); | ||
| 67 | cache.addPrefix(local_cache_directory); | ||
| 68 | cache.addPrefix(global_cache_directory); | ||
| 69 | |||
| 70 | //cache.hash.addBytes(builtin.zig_version); | ||
| 71 | |||
| 46 | const builder = try std.Build.create( | 72 | const builder = try std.Build.create( |
| 47 | allocator, | 73 | allocator, |
| 48 | zig_exe, | 74 | zig_exe, |
| 49 | build_root, | 75 | build_root_directory, |
| 50 | cache_root, | 76 | local_cache_directory, |
| 51 | global_cache_root, | 77 | global_cache_directory, |
| 52 | host, | 78 | host, |
| 79 | &cache, | ||
| 53 | ); | 80 | ); |
| 54 | defer builder.destroy(); | 81 | defer builder.destroy(); |
| 55 | 82 | ||
| ... | @@ -93,6 +120,8 @@ pub fn main() !void { | ... | @@ -93,6 +120,8 @@ pub fn main() !void { |
| 93 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 120 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| 94 | return usageAndErr(builder, false, stderr_stream); | 121 | return usageAndErr(builder, false, stderr_stream); |
| 95 | }; | 122 | }; |
| 123 | } else if (mem.eql(u8, arg, "-l") or mem.eql(u8, arg, "--list-steps")) { | ||
| 124 | return steps(builder, false, stdout_stream); | ||
| 96 | } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { | 125 | } else if (mem.eql(u8, arg, "--prefix-lib-dir")) { |
| 97 | dir_list.lib_dir = nextArg(args, &arg_idx) orelse { | 126 | dir_list.lib_dir = nextArg(args, &arg_idx) orelse { |
| 98 | std.debug.print("Expected argument after {s}\n\n", .{arg}); | 127 | std.debug.print("Expected argument after {s}\n\n", .{arg}); |
| ... | @@ -136,7 +165,7 @@ pub fn main() !void { | ... | @@ -136,7 +165,7 @@ pub fn main() !void { |
| 136 | return usageAndErr(builder, false, stderr_stream); | 165 | return usageAndErr(builder, false, stderr_stream); |
| 137 | }; | 166 | }; |
| 138 | } else if (mem.eql(u8, arg, "--zig-lib-dir")) { | 167 | } else if (mem.eql(u8, arg, "--zig-lib-dir")) { |
| 139 | builder.override_lib_dir = nextArg(args, &arg_idx) orelse { | 168 | builder.zig_lib_dir = nextArg(args, &arg_idx) orelse { |
| 140 | std.debug.print("Expected argument after --zig-lib-dir\n\n", .{}); | 169 | std.debug.print("Expected argument after --zig-lib-dir\n\n", .{}); |
| 141 | return usageAndErr(builder, false, stderr_stream); | 170 | return usageAndErr(builder, false, stderr_stream); |
| 142 | }; | 171 | }; |
| ... | @@ -232,20 +261,13 @@ pub fn main() !void { | ... | @@ -232,20 +261,13 @@ pub fn main() !void { |
| 232 | }; | 261 | }; |
| 233 | } | 262 | } |
| 234 | 263 | ||
| 235 | fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { | 264 | fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { |
| 236 | // run the build script to collect the options | 265 | // run the build script to collect the options |
| 237 | if (!already_ran_build) { | 266 | if (!already_ran_build) { |
| 238 | builder.resolveInstallPrefix(null, .{}); | 267 | builder.resolveInstallPrefix(null, .{}); |
| 239 | try builder.runBuild(root); | 268 | try builder.runBuild(root); |
| 240 | } | 269 | } |
| 241 | 270 | ||
| 242 | try out_stream.print( | ||
| 243 | \\Usage: {s} build [steps] [options] | ||
| 244 | \\ | ||
| 245 | \\Steps: | ||
| 246 | \\ | ||
| 247 | , .{builder.zig_exe}); | ||
| 248 | |||
| 249 | const allocator = builder.allocator; | 271 | const allocator = builder.allocator; |
| 250 | for (builder.top_level_steps.items) |top_level_step| { | 272 | for (builder.top_level_steps.items) |top_level_step| { |
| 251 | const name = if (&top_level_step.step == builder.default_step) | 273 | const name = if (&top_level_step.step == builder.default_step) |
| ... | @@ -254,6 +276,23 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -254,6 +276,23 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 254 | top_level_step.step.name; | 276 | top_level_step.step.name; |
| 255 | try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description }); | 277 | try out_stream.print(" {s:<28} {s}\n", .{ name, top_level_step.description }); |
| 256 | } | 278 | } |
| 279 | } | ||
| 280 | |||
| 281 | fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { | ||
| 282 | // run the build script to collect the options | ||
| 283 | if (!already_ran_build) { | ||
| 284 | builder.resolveInstallPrefix(null, .{}); | ||
| 285 | try builder.runBuild(root); | ||
| 286 | } | ||
| 287 | |||
| 288 | try out_stream.print( | ||
| 289 | \\ | ||
| 290 | \\Usage: {s} build [steps] [options] | ||
| 291 | \\ | ||
| 292 | \\Steps: | ||
| 293 | \\ | ||
| 294 | , .{builder.zig_exe}); | ||
| 295 | try steps(builder, true, out_stream); | ||
| 257 | 296 | ||
| 258 | try out_stream.writeAll( | 297 | try out_stream.writeAll( |
| 259 | \\ | 298 | \\ |
| ... | @@ -284,6 +323,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -284,6 +323,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 284 | \\ Windows programs on Linux hosts. (default: no) | 323 | \\ Windows programs on Linux hosts. (default: no) |
| 285 | \\ | 324 | \\ |
| 286 | \\ -h, --help Print this help and exit | 325 | \\ -h, --help Print this help and exit |
| 326 | \\ -l, --list-steps Print available steps | ||
| 287 | \\ --verbose Print commands before executing them | 327 | \\ --verbose Print commands before executing them |
| 288 | \\ --color [auto|off|on] Enable or disable colored error messages | 328 | \\ --color [auto|off|on] Enable or disable colored error messages |
| 289 | \\ --prominent-compile-errors Output compile errors formatted for a human to read | 329 | \\ --prominent-compile-errors Output compile errors formatted for a human to read |
| ... | @@ -292,6 +332,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi | ... | @@ -292,6 +332,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi |
| 292 | \\ | 332 | \\ |
| 293 | ); | 333 | ); |
| 294 | 334 | ||
| 335 | const allocator = builder.allocator; | ||
| 295 | if (builder.available_options_list.items.len == 0) { | 336 | if (builder.available_options_list.items.len == 0) { |
| 296 | try out_stream.print(" (none)\n", .{}); | 337 | try out_stream.print(" (none)\n", .{}); |
| 297 | } else { | 338 | } else { |
lib/compiler_rt.zig+1| ... | @@ -23,6 +23,7 @@ comptime { | ... | @@ -23,6 +23,7 @@ comptime { |
| 23 | _ = @import("compiler_rt/multf3.zig"); | 23 | _ = @import("compiler_rt/multf3.zig"); |
| 24 | _ = @import("compiler_rt/mulxf3.zig"); | 24 | _ = @import("compiler_rt/mulxf3.zig"); |
| 25 | 25 | ||
| 26 | _ = @import("compiler_rt/powiXf2.zig"); | ||
| 26 | _ = @import("compiler_rt/mulc3.zig"); | 27 | _ = @import("compiler_rt/mulc3.zig"); |
| 27 | _ = @import("compiler_rt/mulhc3.zig"); | 28 | _ = @import("compiler_rt/mulhc3.zig"); |
| 28 | _ = @import("compiler_rt/mulsc3.zig"); | 29 | _ = @import("compiler_rt/mulsc3.zig"); |
lib/compiler_rt/README.md+155-19| ... | @@ -77,15 +77,15 @@ Integer and Float Operations | ... | @@ -77,15 +77,15 @@ Integer and Float Operations |
| 77 | | ✓ | __ucmpdi2 | u64 | u64 | i32 | .. | | 77 | | ✓ | __ucmpdi2 | u64 | u64 | i32 | .. | |
| 78 | | ✓ | __ucmpti2 | u128 | u128 | i32 | .. | | 78 | | ✓ | __ucmpti2 | u128 | u128 | i32 | .. | |
| 79 | | | | | | | **Integer Arithmetic** | | 79 | | | | | | | **Integer Arithmetic** | |
| 80 | | ✗ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] | | 80 | | ✓ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] | |
| 81 | | ✓ | __ashldi3 | i64 | i32 | i64 | .. | | 81 | | ✓ | __ashldi3 | i64 | i32 | i64 | .. | |
| 82 | | ✓ | __ashlti3 | i128 | i32 | i128 | .. | | 82 | | ✓ | __ashlti3 | i128 | i32 | i128 | .. | |
| 83 | | ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM | | 83 | | ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM | |
| 84 | | ✗ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] | | 84 | | ✓ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] | |
| 85 | | ✓ | __ashrdi3 | i64 | i32 | i64 | .. | | 85 | | ✓ | __ashrdi3 | i64 | i32 | i64 | .. | |
| 86 | | ✓ | __ashrti3 | i128 | i32 | i128 | .. | | 86 | | ✓ | __ashrti3 | i128 | i32 | i128 | .. | |
| 87 | | ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM | | 87 | | ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM | |
| 88 | | ✗ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] | | 88 | | ✓ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] | |
| 89 | | ✓ | __lshrdi3 | i64 | i32 | i64 | .. | | 89 | | ✓ | __lshrdi3 | i64 | i32 | i64 | .. | |
| 90 | | ✓ | __lshrti3 | i128 | i32 | i128 | .. | | 90 | | ✓ | __lshrti3 | i128 | i32 | i128 | .. | |
| 91 | | ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM | | 91 | | ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM | |
| ... | @@ -114,7 +114,7 @@ Integer and Float Operations | ... | @@ -114,7 +114,7 @@ Integer and Float Operations |
| 114 | | ✓ | __udivmodti4 | u128 | u128 | u128 | .. | | 114 | | ✓ | __udivmodti4 | u128 | u128 | u128 | .. | |
| 115 | | ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` | | 115 | | ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` | |
| 116 | | ✓ | __divmoddi4 | i64 | i64 | i64 | .. | | 116 | | ✓ | __divmoddi4 | i64 | i64 | i64 | .. | |
| 117 | | ✗ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] | | 117 | | ✓ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] | |
| 118 | | | | | | | **Integer Arithmetic with Trapping Overflow**| | 118 | | | | | | | **Integer Arithmetic with Trapping Overflow**| |
| 119 | | ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) | | 119 | | ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) | |
| 120 | | ✓ | __absvdi2 | i64 | i64 | i64 | .. | | 120 | | ✓ | __absvdi2 | i64 | i64 | i64 | .. | |
| ... | @@ -328,11 +328,11 @@ Integer and Float Operations | ... | @@ -328,11 +328,11 @@ Integer and Float Operations |
| 328 | | ✓ | __negtf2 | f128 | ∅ | f128 | .. | | 328 | | ✓ | __negtf2 | f128 | ∅ | f128 | .. | |
| 329 | | ✓ | __negxf2 | f80 | ∅ | f80 | .. | | 329 | | ✓ | __negxf2 | f80 | ∅ | f80 | .. | |
| 330 | | | | | | | **Floating point raised to integer power** | | 330 | | | | | | | **Floating point raised to integer power** | |
| 331 | | ✗ | __powihf2 | f16 | f16 | f16 | `a ^ b` | | 331 | | ✓ | __powihf2 | f16 | i32 | f16 | `a ^ b` | |
| 332 | | ✗ | __powisf2 | f32 | f32 | f32 | .. | | 332 | | ✓ | __powisf2 | f32 | i32 | f32 | .. | |
| 333 | | ✗ | __powidf2 | f64 | f64 | f64 | .. | | 333 | | ✓ | __powidf2 | f64 | i32 | f64 | .. | |
| 334 | | ✗ | __powitf2 | f128 | f128 | f128 | .. | | 334 | | ✓ | __powitf2 | f128 | i32 | f128 | .. | |
| 335 | | ✗ | __powixf2 | f80 | f80 | f80 | .. | | 335 | | ✓ | __powixf2 | f80 | i32 | f80 | .. | |
| 336 | | ✓ | __mulhc3 | all4 | f16 | f16 | `(a+ib) * (c+id)` | | 336 | | ✓ | __mulhc3 | all4 | f16 | f16 | `(a+ib) * (c+id)` | |
| 337 | | ✓ | __mulsc3 | all4 | f32 | f32 | .. | | 337 | | ✓ | __mulsc3 | all4 | f32 | f32 | .. | |
| 338 | | ✓ | __muldc3 | all4 | f64 | f64 | .. | | 338 | | ✓ | __muldc3 | all4 | f64 | f64 | .. | |
| ... | @@ -552,20 +552,156 @@ TODO brief explanation + implementation | ... | @@ -552,20 +552,156 @@ TODO brief explanation + implementation |
| 552 | | ------ | ------------- | --------- | --------- | --------- | -------------------------- | | 552 | | ------ | ------------- | --------- | --------- | --------- | -------------------------- | |
| 553 | | | | | | | **Fixed-Point Fractional** | | 553 | | | | | | | **Fixed-Point Fractional** | |
| 554 | 554 | ||
| 555 | Further content: | 555 | Math functions according to C99 with gnu extension sincos. f16, f80 and f128 functions |
| 556 | are additionally supported by Zig, but not part of C standard. Alphabetically sorted. | ||
| 557 | |||
| 558 | | Done | Name | a | b | Out | Comment | | ||
| 559 | | ---- | ------- | --------- | --------- | --------- | -------------------------- | | ||
| 560 | | ✓ | __ceilh | f16 | ∅ | f16 |smallest integer value not less than a| | ||
| 561 | | ✓ | ceilf | f32 | ∅ | f32 |If a is integer, +-0, +-NaN, or +-infinite, a itself is returned.| | ||
| 562 | | ✓ | ceil | f64 | ∅ | f64 | .. | | ||
| 563 | | ✓ | __ceilx | f80 | ∅ | f80 | | | ||
| 564 | | ✓ | ceilf128 | f128 | ∅ | f128 | .. PPC | | ||
| 565 | | ✓ | ceilq | f128 | ∅ | f128 | .. | | ||
| 566 | | ✓ | ceill |long double| ∅ |long double| .. | | ||
| 567 | | ✓ | __cosh | f16 | ∅ | f16 | `cos(a)=(e^(ia)+e^(-ia))/2`| | ||
| 568 | | ✓ | cosf | f32 | ∅ | f32 | .. | | ||
| 569 | | ✓ | cos | f64 | ∅ | f64 | .. | | ||
| 570 | | ✓ | __cosx | f80 | ∅ | f80 | .. | | ||
| 571 | | ✓ | cosf128 | f128 | ∅ | f128 | .. | | ||
| 572 | | ✓ | cosq | f128 | ∅ | f128 | .. PPC | | ||
| 573 | | ✓ | cosl |long double| ∅ |long double| .. | | ||
| 574 | | ✓ | __exph | f16 | ∅ | f16 | `e^a` with e base of natural logarithms| | ||
| 575 | | ✓ | expf | f32 | ∅ | f32 | .. | | ||
| 576 | | ✓ | exp | f64 | ∅ | f64 | .. | | ||
| 577 | | ✓ | __expx | f80 | ∅ | f80 | .. | | ||
| 578 | | ✓ | expf128 | f128 | ∅ | f128 | .. | | ||
| 579 | | ✓ | expq | f128 | ∅ | f128 | .. PPC | | ||
| 580 | | ✓ | expl |long double| ∅ |long double| .. | | ||
| 581 | | ✓ | __exp2h | f16 | ∅ | f16 | `2^a` | | ||
| 582 | | ✓ | exp2f | f32 | ∅ | f32 | .. | | ||
| 583 | | ✓ | exp2 | f64 | ∅ | f64 | .. | | ||
| 584 | | ✓ | __exp2x | f80 | ∅ | f80 | .. | | ||
| 585 | | ✓ | exp2f128 | f128 | ∅ | f128 | .. | | ||
| 586 | | ✓ | exp2q | f128 | ∅ | f128 | .. PPC | | ||
| 587 | | ✓ | exp2l |long double| ∅ |long double| .. | | ||
| 588 | | ✓ | __fabsh | f16 | ∅ | f16 | absolute value of a | | ||
| 589 | | ✓ | fabsf | f32 | ∅ | f32 | .. | | ||
| 590 | | ✓ | fabs | f64 | ∅ | f64 | .. | | ||
| 591 | | ✓ | __fabsx | f80 | ∅ | f80 | .. | | ||
| 592 | | ✓ | fabsf128 | f128 | ∅ | f128 | .. | | ||
| 593 | | ✓ | fabsq | f128 | ∅ | f128 | .. PPC | | ||
| 594 | | ✓ | fabsl |long double| ∅ |long double| .. | | ||
| 595 | | ✓ | __floorh | f16 | ∅ | f16 |largest integer value not greater than a| | ||
| 596 | | ✓ | floorf | f32 | ∅ | f32 |If a is integer, +-0, +-NaN, or +-infinite, a itself is returned.| | ||
| 597 | | ✓ | floor | f64 | ∅ | f64 | .. | | ||
| 598 | | ✓ | __floorx | f80 | ∅ | f80 | .. | | ||
| 599 | | ✓ | floorf128 | f128 | ∅ | f128 | .. | | ||
| 600 | | ✓ | floorq | f128 | ∅ | f128 | .. PPC | | ||
| 601 | | ✓ | floorl |long double| ∅ |long double| .. | | ||
| 602 | | ✓ | __fmah | f16 | 2xf16 | f16 | args a,b,c result `(a*b)+c`| | ||
| 603 | | ✓ | fmaf | f32 | 2xf32 | f32 |Fused multiply-add for hardware acceleration| | ||
| 604 | | ✓ | fma | f64 | 2xf64 | f64 | .. | | ||
| 605 | | ✓ | __fmax | f80 | 2xf80 | f80 | .. | | ||
| 606 | | ✓ | fmaf128 | f128 | 2xf128 | f128 | .. | | ||
| 607 | | ✓ | fmaq | f128 | 2xf128 | f128 | .. PPC | | ||
| 608 | | ✓ | fmal |long double|2xlong double|long double| .. | | ||
| 609 | | ✓ | __fmaxh | f16 | f16 | f16 | larger value of a,b | | ||
| 610 | | ✓ | fmaxf | f32 | f32 | f32 | .. | | ||
| 611 | | ✓ | fmax | f64 | f64 | f64 | .. | | ||
| 612 | | ✓ | __fmaxx | f80 | f80 | f80 | .. | | ||
| 613 | | ✓ | fmaxf128 | f128 | f128 | f128 | .. | | ||
| 614 | | ✓ | fmaxq | f128 | f128 | f128 | .. PPC | | ||
| 615 | | ✓ | fmaxl |long double|long double|long double| .. | | ||
| 616 | | ✓ | __fminh | f16 | f16 | f16 | smaller value of a,b | | ||
| 617 | | ✓ | fminf | f32 | f32 | f32 | .. | | ||
| 618 | | ✓ | fmin | f64 | f64 | f64 | .. | | ||
| 619 | | ✓ | __fminx | f80 | f80 | f80 | .. | | ||
| 620 | | ✓ | fminf128 | f128 | f128 | f128 | .. | | ||
| 621 | | ✓ | fminq | f128 | f128 | f128 | .. PPC | | ||
| 622 | | ✓ | fminl |long double|long double|long double| .. | | ||
| 623 | | ✓ | __fmodh | f16 | f16 | f16 |floating-point remainder of division a/b| | ||
| 624 | | ✓ | fmodf | f32 | f32 | f32 | .. | | ||
| 625 | | ✓ | fmod | f64 | f64 | f64 | .. | | ||
| 626 | | ✓ | __fmodx | f80 | f80 | f80 | .. | | ||
| 627 | | ✓ | fmodf128 | f128 | f128 | f128 | .. | | ||
| 628 | | ✓ | fmodq | f128 | f128 | f128 | .. PPC | | ||
| 629 | | ✓ | fmodl |long double|long double|long double| .. | | ||
| 630 | | ✓ | __logh | f16 | ∅ | f16 |natural (base-e) logarithm of a| | ||
| 631 | | ✓ | logf | f32 | ∅ | f32 | .. | | ||
| 632 | | ✓ | log | f64 | ∅ | f64 | .. | | ||
| 633 | | ✓ | __logx | f80 | ∅ | f80 | .. | | ||
| 634 | | ✓ | logf128 | f128 | ∅ | f128 | .. | | ||
| 635 | | ✓ | logq | f128 | ∅ | f128 | .. PPC | | ||
| 636 | | ✓ | logl |long double| ∅ |long double| .. | | ||
| 637 | | ✓ | __log10h | f16 | ∅ | f16 |common (base-10) logarithm of a| | ||
| 638 | | ✓ | log10f | f32 | ∅ | f32 | .. | | ||
| 639 | | ✓ | log10 | f64 | ∅ | f64 | .. | | ||
| 640 | | ✓ | __log10x | f80 | ∅ | f80 | .. | | ||
| 641 | | ✓ | log10f128 | f128 | ∅ | f128 | .. | | ||
| 642 | | ✓ | log10q | f128 | ∅ | f128 | .. PPC | | ||
| 643 | | ✓ | log10l |long double| ∅ |long double| .. | | ||
| 644 | | ✓ | __log2h | f16 | ∅ | f16 | base-2 logarithm of a | | ||
| 645 | | ✓ | log2f | f32 | ∅ | f32 | .. | | ||
| 646 | | ✓ | log2 | f64 | ∅ | f64 | .. | | ||
| 647 | | ✓ | __log2x | f80 | ∅ | f80 | .. | | ||
| 648 | | ✓ | log2f128 | f128 | ∅ | f128 | .. | | ||
| 649 | | ✓ | log2q | f128 | ∅ | f128 | .. PPC | | ||
| 650 | | ✓ | log2l |long double| ∅ |long double| .. | | ||
| 651 | | ✓ | __roundh | f16 | ∅ | f16 | a rounded to next int away from zero| | ||
| 652 | | ✓ | roundf | f32 | ∅ | f32 | .. | | ||
| 653 | | ✓ | round | f64 | ∅ | f64 | .. | | ||
| 654 | | ✓ | __roundx | f80 | ∅ | f80 | .. | | ||
| 655 | | ✓ | roundf128 | f128 | ∅ | f128 | .. | | ||
| 656 | | ✓ | roundq | f128 | ∅ | f128 | .. PPC | | ||
| 657 | | ✓ | roundl |long double| ∅ |long double| .. | | ||
| 658 | | ✓ | __sinh | f16 | ∅ | f16 | `sin(a)=(e^(ia)-e^(-ia))/2`| | ||
| 659 | | ✓ | sinf | f32 | ∅ | f32 | .. | | ||
| 660 | | ✓ | sin | f64 | ∅ | f64 | .. | | ||
| 661 | | ✓ | __sinx | f80 | ∅ | f80 | .. | | ||
| 662 | | ✓ | sinf128 | f128 | ∅ | f128 | .. | | ||
| 663 | | ✓ | sinq | f128 | ∅ | f128 | .. PPC | | ||
| 664 | | ✓ | sinl |long double| ∅ |long double| .. | | ||
| 665 | | ✓ | __sincosh | f16 | 2x *f16 | ∅ |sin and cos of the same angle a| | ||
| 666 | | ✓ | sincosf | f32 | 2x *f32 | ∅ |args a,*b,*c, `b.*=sin(x),c.*=cos(x)`| | ||
| 667 | | ✓ | sincos | f64 | 2x *f64 | ∅ | .. | | ||
| 668 | | ✓ | __sincosx | f80 | 2x *f80 | ∅ | .. | | ||
| 669 | | ✓ | sincosf128 | f128 | 2x *f128 | ∅ | .. | | ||
| 670 | | ✓ | sincosq | f128 | 2x *f128 | ∅ | .. PPC | | ||
| 671 | | ✓ | sincosl |long double| 2x *long double|∅ | .. | | ||
| 672 | | ✓ | __sqrth | f16 | ∅ | f16 | square root of a (find `r st. a=r^2`)| | ||
| 673 | | ✓ | sqrtf | f32 | ∅ | f32 | .. | | ||
| 674 | | ✓ | sqrt | f64 | ∅ | f64 | .. | | ||
| 675 | | ✓ | __sqrtx | f80 | ∅ | f80 | .. | | ||
| 676 | | ✓ | sqrtf128 | f128 | ∅ | f128 | .. | | ||
| 677 | | ✓ | sqrtq | f128 | ∅ | f128 | .. PPC | | ||
| 678 | | ✓ | sqrtl |long double| ∅ |long double| .. | | ||
| 679 | | ✓ | __tanh | f16 | ∅ | f16 | `tan(x)=sin(x)/cos(x) | | ||
| 680 | | ✓ | tanf | f32 | ∅ | f32 | .. | | ||
| 681 | | ✓ | tan | f64 | ∅ | f64 | .. | | ||
| 682 | | ✓ | __tanx | f80 | ∅ | f80 | .. | | ||
| 683 | | ✓ | tanf128 | f128 | ∅ | f128 | .. | | ||
| 684 | | ✓ | tanq | f128 | ∅ | f128 | .. PPC | | ||
| 685 | | ✓ | tanl |long double| ∅ |long double| .. | | ||
| 686 | | ✓ | __trunch | f16 | ∅ | f16 | a rounded to next int towards zero| | ||
| 687 | | ✓ | truncf | f32 | ∅ | f32 | .. | | ||
| 688 | | ✓ | trunc | f64 | ∅ | f64 | .. | | ||
| 689 | | ✓ | __truncx | f80 | ∅ | f80 | .. | | ||
| 690 | | ✓ | truncf128 | f128 | ∅ | f128 | .. | | ||
| 691 | | ✓ | truncq | f128 | ∅ | f128 | .. PPC | | ||
| 692 | | ✓ | truncl |long double| ∅ |long double| .. | | ||
| 693 | |||
| 694 | Further content (conditionally) exported with C abi: | ||
| 556 | - aarch64 outline atomics | 695 | - aarch64 outline atomics |
| 696 | - arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp) | ||
| 557 | - atomics | 697 | - atomics |
| 558 | - msvc things like _alldiv, _aulldiv, _allrem | ||
| 559 | - clear cache | ||
| 560 | - tls emulation | ||
| 561 | - math routines (cos, sin, tan, ceil, floor, exp, exp2, fabs, log, log10, log2, sincos, sqrt) | ||
| 562 | - bcmp | 698 | - bcmp |
| 563 | - ieee float routines (fma, fmax, fmin, fmod, fabs, float rounding, ) | 699 | - clear cache |
| 564 | - arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp) | ||
| 565 | - memory routines (memcmp, memcpy, memset, memmove) | 700 | - memory routines (memcmp, memcpy, memset, memmove) |
| 701 | - msvc things like _alldiv, _aulldiv, _allrem | ||
| 566 | - objective-c __isPlatformVersionAtLeast check | 702 | - objective-c __isPlatformVersionAtLeast check |
| 567 | - stack probe routines | 703 | - stack probe routines |
| 704 | - tls emulation | ||
| 568 | 705 | ||
| 569 | Future work | 706 | Future work: |
| 570 | 707 | - Arbitrary length integer library routines | |
| 571 | Arbitrary length integer library routines |
lib/compiler_rt/ashldi3_test.zig deleted-32| ... | @@ -1,32 +0,0 @@ | ||
| 1 | const __ashldi3 = @import("shift.zig").__ashldi3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__ashldi3(a: i64, b: i32, expected: u64) !void { | ||
| 5 | const x = __ashldi3(a, b); | ||
| 6 | try testing.expectEqual(@bitCast(i64, expected), x); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "ashldi3" { | ||
| 10 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF); | ||
| 11 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x2468ACF13579BDE); | ||
| 12 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37BC); | ||
| 13 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x91A2B3C4D5E6F78); | ||
| 14 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDEF0); | ||
| 15 | |||
| 16 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x789ABCDEF0000000); | ||
| 17 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0xF13579BDE0000000); | ||
| 18 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0xE26AF37BC0000000); | ||
| 19 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0xC4D5E6F780000000); | ||
| 20 | |||
| 21 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x89ABCDEF00000000); | ||
| 22 | |||
| 23 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x13579BDE00000000); | ||
| 24 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x26AF37BC00000000); | ||
| 25 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x4D5E6F7800000000); | ||
| 26 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x9ABCDEF000000000); | ||
| 27 | |||
| 28 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0xF000000000000000); | ||
| 29 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0xE000000000000000); | ||
| 30 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0xC000000000000000); | ||
| 31 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0x8000000000000000); | ||
| 32 | } | ||
lib/compiler_rt/ashlti3_test.zig deleted-46| ... | @@ -1,46 +0,0 @@ | ||
| 1 | const __ashlti3 = @import("shift.zig").__ashlti3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__ashlti3(a: i128, b: i32, expected: i128) !void { | ||
| 5 | const x = __ashlti3(a, b); | ||
| 6 | try testing.expectEqual(expected, x); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "ashlti3" { | ||
| 10 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215))); | ||
| 11 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642BFDB97530ECA8642A))); | ||
| 12 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C857FB72EA61D950C854))); | ||
| 13 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190AFF6E5D4C3B2A190A8))); | ||
| 14 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xEDCBA9876543215FEDCBA98765432150))); | ||
| 15 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x876543215FEDCBA98765432150000000))); | ||
| 16 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x0ECA8642BFDB97530ECA8642A0000000))); | ||
| 17 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x1D950C857FB72EA61D950C8540000000))); | ||
| 18 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x3B2A190AFF6E5D4C3B2A190A80000000))); | ||
| 19 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x76543215FEDCBA987654321500000000))); | ||
| 20 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xECA8642BFDB97530ECA8642A00000000))); | ||
| 21 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xD950C857FB72EA61D950C85400000000))); | ||
| 22 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xB2A190AFF6E5D4C3B2A190A800000000))); | ||
| 23 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x6543215FEDCBA9876543215000000000))); | ||
| 24 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x5FEDCBA9876543215000000000000000))); | ||
| 25 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xBFDB97530ECA8642A000000000000000))); | ||
| 26 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x7FB72EA61D950C854000000000000000))); | ||
| 27 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190A8000000000000000))); | ||
| 28 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFEDCBA98765432150000000000000000))); | ||
| 29 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642A0000000000000000))); | ||
| 30 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C8540000000000000000))); | ||
| 31 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190A80000000000000000))); | ||
| 32 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xEDCBA987654321500000000000000000))); | ||
| 33 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x87654321500000000000000000000000))); | ||
| 34 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x0ECA8642A00000000000000000000000))); | ||
| 35 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x1D950C85400000000000000000000000))); | ||
| 36 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x3B2A190A800000000000000000000000))); | ||
| 37 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x76543215000000000000000000000000))); | ||
| 38 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xECA8642A000000000000000000000000))); | ||
| 39 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xD950C854000000000000000000000000))); | ||
| 40 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xB2A190A8000000000000000000000000))); | ||
| 41 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x65432150000000000000000000000000))); | ||
| 42 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x50000000000000000000000000000000))); | ||
| 43 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xA0000000000000000000000000000000))); | ||
| 44 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x40000000000000000000000000000000))); | ||
| 45 | try test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x80000000000000000000000000000000))); | ||
| 46 | } | ||
lib/compiler_rt/ashrdi3_test.zig deleted-55| ... | @@ -1,55 +0,0 @@ | ||
| 1 | const __ashrdi3 = @import("shift.zig").__ashrdi3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__ashrdi3(a: i64, b: i32, expected: u64) !void { | ||
| 5 | const x = __ashrdi3(a, b); | ||
| 6 | try testing.expectEqual(@bitCast(i64, expected), x); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "ashrdi3" { | ||
| 10 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF); | ||
| 11 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7); | ||
| 12 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B); | ||
| 13 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD); | ||
| 14 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE); | ||
| 15 | |||
| 16 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678); | ||
| 17 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C); | ||
| 18 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E); | ||
| 19 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF); | ||
| 20 | |||
| 21 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567); | ||
| 22 | |||
| 23 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3); | ||
| 24 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159); | ||
| 25 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC); | ||
| 26 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456); | ||
| 27 | |||
| 28 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0); | ||
| 29 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0); | ||
| 30 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0); | ||
| 31 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0); | ||
| 32 | |||
| 33 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210); | ||
| 34 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0xFF6E5D4C3B2A1908); | ||
| 35 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0xFFB72EA61D950C84); | ||
| 36 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0xFFDB97530ECA8642); | ||
| 37 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFFEDCBA987654321); | ||
| 38 | |||
| 39 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFFFFFFFFEDCBA987); | ||
| 40 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0xFFFFFFFFF6E5D4C3); | ||
| 41 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0xFFFFFFFFFB72EA61); | ||
| 42 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0xFFFFFFFFFDB97530); | ||
| 43 | |||
| 44 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFFFFFFFFFEDCBA98); | ||
| 45 | |||
| 46 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0xFFFFFFFFFF6E5D4C); | ||
| 47 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0xFFFFFFFFFFB72EA6); | ||
| 48 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0xFFFFFFFFFFDB9753); | ||
| 49 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFFFFFFFFFFEDCBA9); | ||
| 50 | |||
| 51 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xFFFFFFFFFFFFFFFA); | ||
| 52 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0xFFFFFFFFFFFFFFFD); | ||
| 53 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0xFFFFFFFFFFFFFFFE); | ||
| 54 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0xFFFFFFFFFFFFFFFF); | ||
| 55 | } | ||
lib/compiler_rt/ashrti3_test.zig deleted-56| ... | @@ -1,56 +0,0 @@ | ||
| 1 | const __ashrti3 = @import("shift.zig").__ashrti3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__ashrti3(a: i128, b: i32, expected: i128) !void { | ||
| 5 | const x = __ashrti3(a, b); | ||
| 6 | try testing.expectEqual(expected, x); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "ashrti3" { | ||
| 10 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215))); | ||
| 11 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190AFF6E5D4C3B2A190A))); | ||
| 12 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFFB72EA61D950C857FB72EA61D950C85))); | ||
| 13 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xFFDB97530ECA8642BFDB97530ECA8642))); | ||
| 14 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xFFEDCBA9876543215FEDCBA987654321))); | ||
| 15 | |||
| 16 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0xFFFFFFFFEDCBA9876543215FEDCBA987))); | ||
| 17 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0xFFFFFFFFF6E5D4C3B2A190AFF6E5D4C3))); | ||
| 18 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFB72EA61D950C857FB72EA61))); | ||
| 19 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFDB97530ECA8642BFDB97530))); | ||
| 20 | |||
| 21 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFEDCBA9876543215FEDCBA98))); | ||
| 22 | |||
| 23 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFF6E5D4C3B2A190AFF6E5D4C))); | ||
| 24 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFB72EA61D950C857FB72EA6))); | ||
| 25 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFDB97530ECA8642BFDB9753))); | ||
| 26 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFEDCBA9876543215FEDCBA9))); | ||
| 27 | |||
| 28 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFEDCBA9876543215F))); | ||
| 29 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFF6E5D4C3B2A190AF))); | ||
| 30 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFB72EA61D950C857))); | ||
| 31 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFDB97530ECA8642B))); | ||
| 32 | |||
| 33 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFEDCBA9876543215))); | ||
| 34 | |||
| 35 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFF6E5D4C3B2A190A))); | ||
| 36 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFB72EA61D950C85))); | ||
| 37 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFDB97530ECA8642))); | ||
| 38 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFEDCBA987654321))); | ||
| 39 | |||
| 40 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFEDCBA987))); | ||
| 41 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C3))); | ||
| 42 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFB72EA61))); | ||
| 43 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFDB97530))); | ||
| 44 | |||
| 45 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA98))); | ||
| 46 | |||
| 47 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C))); | ||
| 48 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFB72EA6))); | ||
| 49 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFDB9753))); | ||
| 50 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA9))); | ||
| 51 | |||
| 52 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))); | ||
| 53 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))); | ||
| 54 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))); | ||
| 55 | try test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))); | ||
| 56 | } | ||
lib/compiler_rt/int.zig+65-3| ... | @@ -9,10 +9,12 @@ const arch = builtin.cpu.arch; | ... | @@ -9,10 +9,12 @@ const arch = builtin.cpu.arch; |
| 9 | const is_test = builtin.is_test; | 9 | const is_test = builtin.is_test; |
| 10 | const common = @import("common.zig"); | 10 | const common = @import("common.zig"); |
| 11 | const udivmod = @import("udivmod.zig").udivmod; | 11 | const udivmod = @import("udivmod.zig").udivmod; |
| 12 | const __divti3 = @import("divti3.zig").__divti3; | ||
| 12 | 13 | ||
| 13 | pub const panic = common.panic; | 14 | pub const panic = common.panic; |
| 14 | 15 | ||
| 15 | comptime { | 16 | comptime { |
| 17 | @export(__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 16 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility }); | 18 | @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility }); |
| 17 | @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); | 19 | @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); |
| 18 | @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility }); | 20 | @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility }); |
| ... | @@ -33,12 +35,72 @@ comptime { | ... | @@ -33,12 +35,72 @@ comptime { |
| 33 | @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = common.linkage, .visibility = common.visibility }); | 35 | @export(__udivmodsi4, .{ .name = "__udivmodsi4", .linkage = common.linkage, .visibility = common.visibility }); |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 38 | pub fn __divmodti4(a: i128, b: i128, rem: *i128) callconv(.C) i128 { | ||
| 39 | const d = __divti3(a, b); | ||
| 40 | rem.* = a -% (d * b); | ||
| 41 | return d; | ||
| 42 | } | ||
| 43 | |||
| 44 | test "test_divmodti4" { | ||
| 45 | const cases = [_][4]i128{ | ||
| 46 | [_]i128{ 0, 1, 0, 0 }, | ||
| 47 | [_]i128{ 0, -1, 0, 0 }, | ||
| 48 | [_]i128{ 2, 1, 2, 0 }, | ||
| 49 | [_]i128{ 2, -1, -2, 0 }, | ||
| 50 | [_]i128{ -2, 1, -2, 0 }, | ||
| 51 | [_]i128{ -2, -1, 2, 0 }, | ||
| 52 | [_]i128{ 7, 5, 1, 2 }, | ||
| 53 | [_]i128{ -7, 5, -1, -2 }, | ||
| 54 | [_]i128{ 19, 5, 3, 4 }, | ||
| 55 | [_]i128{ 19, -5, -3, 4 }, | ||
| 56 | [_]i128{ @bitCast(i128, @as(u128, 0x80000000000000000000000000000000)), 8, @bitCast(i128, @as(u128, 0xf0000000000000000000000000000000)), 0 }, | ||
| 57 | [_]i128{ @bitCast(i128, @as(u128, 0x80000000000000000000000000000007)), 8, @bitCast(i128, @as(u128, 0xf0000000000000000000000000000001)), -1 }, | ||
| 58 | }; | ||
| 59 | |||
| 60 | for (cases) |case| { | ||
| 61 | try test_one_divmodti4(case[0], case[1], case[2], case[3]); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | fn test_one_divmodti4(a: i128, b: i128, expected_q: i128, expected_r: i128) !void { | ||
| 66 | var r: i128 = undefined; | ||
| 67 | const q: i128 = __divmodti4(a, b, &r); | ||
| 68 | try testing.expect(q == expected_q and r == expected_r); | ||
| 69 | } | ||
| 70 | |||
| 36 | pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 { | 71 | pub fn __divmoddi4(a: i64, b: i64, rem: *i64) callconv(.C) i64 { |
| 37 | const d = __divdi3(a, b); | 72 | const d = __divdi3(a, b); |
| 38 | rem.* = a -% (d *% b); | 73 | rem.* = a -% (d * b); |
| 39 | return d; | 74 | return d; |
| 40 | } | 75 | } |
| 41 | 76 | ||
| 77 | test "test_divmoddi4" { | ||
| 78 | const cases = [_][4]i64{ | ||
| 79 | [_]i64{ 0, 1, 0, 0 }, | ||
| 80 | [_]i64{ 0, -1, 0, 0 }, | ||
| 81 | [_]i64{ 2, 1, 2, 0 }, | ||
| 82 | [_]i64{ 2, -1, -2, 0 }, | ||
| 83 | [_]i64{ -2, 1, -2, 0 }, | ||
| 84 | [_]i64{ -2, -1, 2, 0 }, | ||
| 85 | [_]i64{ 7, 5, 1, 2 }, | ||
| 86 | [_]i64{ -7, 5, -1, -2 }, | ||
| 87 | [_]i64{ 19, 5, 3, 4 }, | ||
| 88 | [_]i64{ 19, -5, -3, 4 }, | ||
| 89 | [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000000)), 8, @bitCast(i64, @as(u64, 0xf000000000000000)), 0 }, | ||
| 90 | [_]i64{ @bitCast(i64, @as(u64, 0x8000000000000007)), 8, @bitCast(i64, @as(u64, 0xf000000000000001)), -1 }, | ||
| 91 | }; | ||
| 92 | |||
| 93 | for (cases) |case| { | ||
| 94 | try test_one_divmoddi4(case[0], case[1], case[2], case[3]); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | fn test_one_divmoddi4(a: i64, b: i64, expected_q: i64, expected_r: i64) !void { | ||
| 99 | var r: i64 = undefined; | ||
| 100 | const q: i64 = __divmoddi4(a, b, &r); | ||
| 101 | try testing.expect(q == expected_q and r == expected_r); | ||
| 102 | } | ||
| 103 | |||
| 42 | pub fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) callconv(.C) u64 { | 104 | pub fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?*u64) callconv(.C) u64 { |
| 43 | return udivmod(u64, a, b, maybe_rem); | 105 | return udivmod(u64, a, b, maybe_rem); |
| 44 | } | 106 | } |
| ... | @@ -424,7 +486,7 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) !void { | ... | @@ -424,7 +486,7 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) !void { |
| 424 | } | 486 | } |
| 425 | 487 | ||
| 426 | pub fn __modsi3(n: i32, d: i32) callconv(.C) i32 { | 488 | pub fn __modsi3(n: i32, d: i32) callconv(.C) i32 { |
| 427 | return n -% __divsi3(n, d) *% d; | 489 | return n -% __divsi3(n, d) * d; |
| 428 | } | 490 | } |
| 429 | 491 | ||
| 430 | test "test_modsi3" { | 492 | test "test_modsi3" { |
| ... | @@ -453,7 +515,7 @@ fn test_one_modsi3(a: i32, b: i32, expected_r: i32) !void { | ... | @@ -453,7 +515,7 @@ fn test_one_modsi3(a: i32, b: i32, expected_r: i32) !void { |
| 453 | } | 515 | } |
| 454 | 516 | ||
| 455 | pub fn __umodsi3(n: u32, d: u32) callconv(.C) u32 { | 517 | pub fn __umodsi3(n: u32, d: u32) callconv(.C) u32 { |
| 456 | return n -% __udivsi3(n, d) *% d; | 518 | return n -% __udivsi3(n, d) * d; |
| 457 | } | 519 | } |
| 458 | 520 | ||
| 459 | test "test_umodsi3" { | 521 | test "test_umodsi3" { |
lib/compiler_rt/lshrdi3_test.zig deleted-55| ... | @@ -1,55 +0,0 @@ | ||
| 1 | const __lshrdi3 = @import("shift.zig").__lshrdi3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__lshrdi3(a: i64, b: i32, expected: u64) !void { | ||
| 5 | const x = __lshrdi3(a, b); | ||
| 6 | try testing.expectEqual(@bitCast(i64, expected), x); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "lshrdi3" { | ||
| 10 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF); | ||
| 11 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7); | ||
| 12 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B); | ||
| 13 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD); | ||
| 14 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE); | ||
| 15 | |||
| 16 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678); | ||
| 17 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C); | ||
| 18 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E); | ||
| 19 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF); | ||
| 20 | |||
| 21 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567); | ||
| 22 | |||
| 23 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3); | ||
| 24 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159); | ||
| 25 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC); | ||
| 26 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456); | ||
| 27 | |||
| 28 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0); | ||
| 29 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0); | ||
| 30 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0); | ||
| 31 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0); | ||
| 32 | |||
| 33 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210); | ||
| 34 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0x7F6E5D4C3B2A1908); | ||
| 35 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0x3FB72EA61D950C84); | ||
| 36 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0x1FDB97530ECA8642); | ||
| 37 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFEDCBA987654321); | ||
| 38 | |||
| 39 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFEDCBA987); | ||
| 40 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0x7F6E5D4C3); | ||
| 41 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0x3FB72EA61); | ||
| 42 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0x1FDB97530); | ||
| 43 | |||
| 44 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFEDCBA98); | ||
| 45 | |||
| 46 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0x7F6E5D4C); | ||
| 47 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0x3FB72EA6); | ||
| 48 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0x1FDB9753); | ||
| 49 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFEDCBA9); | ||
| 50 | |||
| 51 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xA); | ||
| 52 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0x5); | ||
| 53 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0x2); | ||
| 54 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0x1); | ||
| 55 | } | ||
lib/compiler_rt/lshrti3_test.zig deleted-46| ... | @@ -1,46 +0,0 @@ | ||
| 1 | const __lshrti3 = @import("shift.zig").__lshrti3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__lshrti3(a: i128, b: i32, expected: i128) !void { | ||
| 5 | const x = __lshrti3(a, b); | ||
| 6 | try testing.expectEqual(expected, x); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "lshrti3" { | ||
| 10 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215))); | ||
| 11 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0x7F6E5D4C3B2A190AFF6E5D4C3B2A190A))); | ||
| 12 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0x3FB72EA61D950C857FB72EA61D950C85))); | ||
| 13 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0x1FDB97530ECA8642BFDB97530ECA8642))); | ||
| 14 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0x0FEDCBA9876543215FEDCBA987654321))); | ||
| 15 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x0000000FEDCBA9876543215FEDCBA987))); | ||
| 16 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x00000007F6E5D4C3B2A190AFF6E5D4C3))); | ||
| 17 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x00000003FB72EA61D950C857FB72EA61))); | ||
| 18 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x00000001FDB97530ECA8642BFDB97530))); | ||
| 19 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x00000000FEDCBA9876543215FEDCBA98))); | ||
| 20 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0x000000007F6E5D4C3B2A190AFF6E5D4C))); | ||
| 21 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0x000000003FB72EA61D950C857FB72EA6))); | ||
| 22 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0x000000001FDB97530ECA8642BFDB9753))); | ||
| 23 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x000000000FEDCBA9876543215FEDCBA9))); | ||
| 24 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x000000000000000FEDCBA9876543215F))); | ||
| 25 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0x0000000000000007F6E5D4C3B2A190AF))); | ||
| 26 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x0000000000000003FB72EA61D950C857))); | ||
| 27 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0x0000000000000001FDB97530ECA8642B))); | ||
| 28 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0x0000000000000000FEDCBA9876543215))); | ||
| 29 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0x00000000000000007F6E5D4C3B2A190A))); | ||
| 30 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0x00000000000000003FB72EA61D950C85))); | ||
| 31 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0x00000000000000001FDB97530ECA8642))); | ||
| 32 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0x00000000000000000FEDCBA987654321))); | ||
| 33 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x00000000000000000000000FEDCBA987))); | ||
| 34 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x000000000000000000000007F6E5D4C3))); | ||
| 35 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x000000000000000000000003FB72EA61))); | ||
| 36 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x000000000000000000000001FDB97530))); | ||
| 37 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x000000000000000000000000FEDCBA98))); | ||
| 38 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0x0000000000000000000000007F6E5D4C))); | ||
| 39 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0x0000000000000000000000003FB72EA6))); | ||
| 40 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0x0000000000000000000000001FDB9753))); | ||
| 41 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000FEDCBA9))); | ||
| 42 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000000000F))); | ||
| 43 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000007))); | ||
| 44 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000003))); | ||
| 45 | try test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000001))); | ||
| 46 | } | ||
lib/compiler_rt/powiXf2.zig created+58| ... | @@ -0,0 +1,58 @@ | ||
| 1 | //! a raised to integer power of b | ||
| 2 | //! ported from https://github.com/llvm-mirror/compiler-rt/blob/release_80/lib/builtins/powisf2.c | ||
| 3 | //! Multiplication order (left-to-right or right-to-left) does not matter for | ||
| 4 | //! error propagation and this method is optimized for performance, not accuracy. | ||
| 5 | |||
| 6 | const builtin = @import("builtin"); | ||
| 7 | const common = @import("common.zig"); | ||
| 8 | const std = @import("std"); | ||
| 9 | |||
| 10 | pub const panic = common.panic; | ||
| 11 | |||
| 12 | comptime { | ||
| 13 | @export(__powihf2, .{ .name = "__powihf2", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 14 | @export(__powisf2, .{ .name = "__powisf2", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 15 | @export(__powidf2, .{ .name = "__powidf2", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 16 | @export(__powitf2, .{ .name = "__powitf2", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 17 | @export(__powixf2, .{ .name = "__powixf2", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 18 | } | ||
| 19 | |||
| 20 | inline fn powiXf2(comptime FT: type, a: FT, b: i32) FT { | ||
| 21 | var x_a: FT = a; | ||
| 22 | var x_b: i32 = b; | ||
| 23 | const is_recip: bool = b < 0; | ||
| 24 | var r: FT = 1.0; | ||
| 25 | while (true) { | ||
| 26 | if (@bitCast(u32, x_b) & @as(u32, 1) != 0) { | ||
| 27 | r *= x_a; | ||
| 28 | } | ||
| 29 | x_b = @divTrunc(x_b, @as(i32, 2)); | ||
| 30 | if (x_b == 0) break; | ||
| 31 | x_a *= x_a; // Multiplication of x_a propagates the error | ||
| 32 | } | ||
| 33 | return if (is_recip) 1 / r else r; | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn __powihf2(a: f16, b: i32) callconv(.C) f16 { | ||
| 37 | return powiXf2(f16, a, b); | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn __powisf2(a: f32, b: i32) callconv(.C) f32 { | ||
| 41 | return powiXf2(f32, a, b); | ||
| 42 | } | ||
| 43 | |||
| 44 | pub fn __powidf2(a: f64, b: i32) callconv(.C) f64 { | ||
| 45 | return powiXf2(f64, a, b); | ||
| 46 | } | ||
| 47 | |||
| 48 | pub fn __powitf2(a: f128, b: i32) callconv(.C) f128 { | ||
| 49 | return powiXf2(f128, a, b); | ||
| 50 | } | ||
| 51 | |||
| 52 | pub fn __powixf2(a: f80, b: i32) callconv(.C) f80 { | ||
| 53 | return powiXf2(f80, a, b); | ||
| 54 | } | ||
| 55 | |||
| 56 | test { | ||
| 57 | _ = @import("powiXf2_test.zig"); | ||
| 58 | } | ||
lib/compiler_rt/powiXf2_test.zig created+556| ... | @@ -0,0 +1,556 @@ | ||
| 1 | // ported from https://github.com/llvm-mirror/compiler-rt/blob/release_80/test/builtins/Unit/ | ||
| 2 | // powisf2_test.c, powidf2_test.c, powitf2_test.c, powixf2_test.c | ||
| 3 | // powihf2 adapted from powisf2 tests | ||
| 4 | |||
| 5 | const powiXf2 = @import("powiXf2.zig"); | ||
| 6 | const testing = @import("std").testing; | ||
| 7 | const math = @import("std").math; | ||
| 8 | |||
| 9 | fn test__powihf2(a: f16, b: i32, expected: f16) !void { | ||
| 10 | var result = powiXf2.__powihf2(a, b); | ||
| 11 | try testing.expectEqual(expected, result); | ||
| 12 | } | ||
| 13 | |||
| 14 | fn test__powisf2(a: f32, b: i32, expected: f32) !void { | ||
| 15 | var result = powiXf2.__powisf2(a, b); | ||
| 16 | try testing.expectEqual(expected, result); | ||
| 17 | } | ||
| 18 | |||
| 19 | fn test__powidf2(a: f64, b: i32, expected: f64) !void { | ||
| 20 | var result = powiXf2.__powidf2(a, b); | ||
| 21 | try testing.expectEqual(expected, result); | ||
| 22 | } | ||
| 23 | |||
| 24 | fn test__powitf2(a: f128, b: i32, expected: f128) !void { | ||
| 25 | var result = powiXf2.__powitf2(a, b); | ||
| 26 | try testing.expectEqual(expected, result); | ||
| 27 | } | ||
| 28 | |||
| 29 | fn test__powixf2(a: f80, b: i32, expected: f80) !void { | ||
| 30 | var result = powiXf2.__powixf2(a, b); | ||
| 31 | try testing.expectEqual(expected, result); | ||
| 32 | } | ||
| 33 | |||
| 34 | test "powihf2" { | ||
| 35 | try test__powisf2(0, 0, 1); | ||
| 36 | try test__powihf2(1, 0, 1); | ||
| 37 | try test__powihf2(1.5, 0, 1); | ||
| 38 | try test__powihf2(2, 0, 1); | ||
| 39 | try test__powihf2(math.inf_f16, 0, 1); | ||
| 40 | |||
| 41 | try test__powihf2(-0.0, 0, 1); | ||
| 42 | try test__powihf2(-1, 0, 1); | ||
| 43 | try test__powihf2(-1.5, 0, 1); | ||
| 44 | try test__powihf2(-2, 0, 1); | ||
| 45 | try test__powihf2(-math.inf_f16, 0, 1); | ||
| 46 | |||
| 47 | try test__powihf2(0, 1, 0); | ||
| 48 | try test__powihf2(0, 2, 0); | ||
| 49 | try test__powihf2(0, 3, 0); | ||
| 50 | try test__powihf2(0, 4, 0); | ||
| 51 | try test__powihf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 52 | try test__powihf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0); | ||
| 53 | |||
| 54 | try test__powihf2(-0.0, 1, -0.0); | ||
| 55 | try test__powihf2(-0.0, 2, 0); | ||
| 56 | try test__powihf2(-0.0, 3, -0.0); | ||
| 57 | try test__powihf2(-0.0, 4, 0); | ||
| 58 | try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 59 | try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0); | ||
| 60 | |||
| 61 | try test__powihf2(1, 1, 1); | ||
| 62 | try test__powihf2(1, 2, 1); | ||
| 63 | try test__powihf2(1, 3, 1); | ||
| 64 | try test__powihf2(1, 4, 1); | ||
| 65 | try test__powihf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1); | ||
| 66 | try test__powihf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1); | ||
| 67 | |||
| 68 | try test__powihf2(math.inf_f16, 1, math.inf_f16); | ||
| 69 | try test__powihf2(math.inf_f16, 2, math.inf_f16); | ||
| 70 | try test__powihf2(math.inf_f16, 3, math.inf_f16); | ||
| 71 | try test__powihf2(math.inf_f16, 4, math.inf_f16); | ||
| 72 | try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f16); | ||
| 73 | try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f16); | ||
| 74 | |||
| 75 | try test__powihf2(-math.inf_f16, 1, -math.inf_f16); | ||
| 76 | try test__powihf2(-math.inf_f16, 2, math.inf_f16); | ||
| 77 | try test__powihf2(-math.inf_f16, 3, -math.inf_f16); | ||
| 78 | try test__powihf2(-math.inf_f16, 4, math.inf_f16); | ||
| 79 | try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f16); | ||
| 80 | try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f16); | ||
| 81 | // | ||
| 82 | try test__powihf2(0, -1, math.inf_f16); | ||
| 83 | try test__powihf2(0, -2, math.inf_f16); | ||
| 84 | try test__powihf2(0, -3, math.inf_f16); | ||
| 85 | try test__powihf2(0, -4, math.inf_f16); | ||
| 86 | try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f16); // 0 ^ anything = +inf | ||
| 87 | try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f16); | ||
| 88 | try test__powihf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f16); | ||
| 89 | |||
| 90 | try test__powihf2(-0.0, -1, -math.inf_f16); | ||
| 91 | try test__powihf2(-0.0, -2, math.inf_f16); | ||
| 92 | try test__powihf2(-0.0, -3, -math.inf_f16); | ||
| 93 | try test__powihf2(-0.0, -4, math.inf_f16); | ||
| 94 | try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f16); // -0 ^ anything even = +inf | ||
| 95 | try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f16); // -0 ^ anything odd = -inf | ||
| 96 | try test__powihf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f16); | ||
| 97 | |||
| 98 | try test__powihf2(1, -1, 1); | ||
| 99 | try test__powihf2(1, -2, 1); | ||
| 100 | try test__powihf2(1, -3, 1); | ||
| 101 | try test__powihf2(1, -4, 1); | ||
| 102 | try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1); // 1.0 ^ anything = 1 | ||
| 103 | try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1); | ||
| 104 | try test__powihf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1); | ||
| 105 | |||
| 106 | try test__powihf2(math.inf_f16, -1, 0); | ||
| 107 | try test__powihf2(math.inf_f16, -2, 0); | ||
| 108 | try test__powihf2(math.inf_f16, -3, 0); | ||
| 109 | try test__powihf2(math.inf_f16, -4, 0); | ||
| 110 | try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 111 | try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000001)), 0); | ||
| 112 | try test__powihf2(math.inf_f16, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 113 | // | ||
| 114 | try test__powihf2(-math.inf_f16, -1, -0.0); | ||
| 115 | try test__powihf2(-math.inf_f16, -2, 0); | ||
| 116 | try test__powihf2(-math.inf_f16, -3, -0.0); | ||
| 117 | try test__powihf2(-math.inf_f16, -4, 0); | ||
| 118 | try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 119 | try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000001)), -0.0); | ||
| 120 | try test__powihf2(-math.inf_f16, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 121 | |||
| 122 | try test__powihf2(2, 10, 1024.0); | ||
| 123 | try test__powihf2(-2, 10, 1024.0); | ||
| 124 | try test__powihf2(2, -10, 1.0 / 1024.0); | ||
| 125 | try test__powihf2(-2, -10, 1.0 / 1024.0); | ||
| 126 | |||
| 127 | try test__powihf2(2, 14, 16384.0); | ||
| 128 | try test__powihf2(-2, 14, 16384.0); | ||
| 129 | try test__powihf2(2, 15, 32768.0); | ||
| 130 | try test__powihf2(-2, 15, -32768.0); | ||
| 131 | try test__powihf2(2, 16, math.inf_f16); | ||
| 132 | try test__powihf2(-2, 16, math.inf_f16); | ||
| 133 | |||
| 134 | try test__powihf2(2, -13, 1.0 / 8192.0); | ||
| 135 | try test__powihf2(-2, -13, -1.0 / 8192.0); | ||
| 136 | try test__powihf2(2, -15, 1.0 / 32768.0); | ||
| 137 | try test__powihf2(-2, -15, -1.0 / 32768.0); | ||
| 138 | try test__powihf2(2, -16, 0.0); // expected = 0.0 = 1/(-2**16) | ||
| 139 | try test__powihf2(-2, -16, 0.0); // expected = 0.0 = 1/(2**16) | ||
| 140 | } | ||
| 141 | |||
| 142 | test "powisf2" { | ||
| 143 | try test__powisf2(0, 0, 1); | ||
| 144 | try test__powisf2(1, 0, 1); | ||
| 145 | try test__powisf2(1.5, 0, 1); | ||
| 146 | try test__powisf2(2, 0, 1); | ||
| 147 | try test__powisf2(math.inf_f32, 0, 1); | ||
| 148 | |||
| 149 | try test__powisf2(-0.0, 0, 1); | ||
| 150 | try test__powisf2(-1, 0, 1); | ||
| 151 | try test__powisf2(-1.5, 0, 1); | ||
| 152 | try test__powisf2(-2, 0, 1); | ||
| 153 | try test__powisf2(-math.inf_f32, 0, 1); | ||
| 154 | |||
| 155 | try test__powisf2(0, 1, 0); | ||
| 156 | try test__powisf2(0, 2, 0); | ||
| 157 | try test__powisf2(0, 3, 0); | ||
| 158 | try test__powisf2(0, 4, 0); | ||
| 159 | try test__powisf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 160 | try test__powisf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0); | ||
| 161 | |||
| 162 | try test__powisf2(-0.0, 1, -0.0); | ||
| 163 | try test__powisf2(-0.0, 2, 0); | ||
| 164 | try test__powisf2(-0.0, 3, -0.0); | ||
| 165 | try test__powisf2(-0.0, 4, 0); | ||
| 166 | try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 167 | try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0); | ||
| 168 | |||
| 169 | try test__powisf2(1, 1, 1); | ||
| 170 | try test__powisf2(1, 2, 1); | ||
| 171 | try test__powisf2(1, 3, 1); | ||
| 172 | try test__powisf2(1, 4, 1); | ||
| 173 | try test__powisf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1); | ||
| 174 | try test__powisf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1); | ||
| 175 | |||
| 176 | try test__powisf2(math.inf_f32, 1, math.inf_f32); | ||
| 177 | try test__powisf2(math.inf_f32, 2, math.inf_f32); | ||
| 178 | try test__powisf2(math.inf_f32, 3, math.inf_f32); | ||
| 179 | try test__powisf2(math.inf_f32, 4, math.inf_f32); | ||
| 180 | try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f32); | ||
| 181 | try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f32); | ||
| 182 | |||
| 183 | try test__powisf2(-math.inf_f32, 1, -math.inf_f32); | ||
| 184 | try test__powisf2(-math.inf_f32, 2, math.inf_f32); | ||
| 185 | try test__powisf2(-math.inf_f32, 3, -math.inf_f32); | ||
| 186 | try test__powisf2(-math.inf_f32, 4, math.inf_f32); | ||
| 187 | try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f32); | ||
| 188 | try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f32); | ||
| 189 | |||
| 190 | try test__powisf2(0, -1, math.inf_f32); | ||
| 191 | try test__powisf2(0, -2, math.inf_f32); | ||
| 192 | try test__powisf2(0, -3, math.inf_f32); | ||
| 193 | try test__powisf2(0, -4, math.inf_f32); | ||
| 194 | try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f32); | ||
| 195 | try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f32); | ||
| 196 | try test__powisf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f32); | ||
| 197 | |||
| 198 | try test__powisf2(-0.0, -1, -math.inf_f32); | ||
| 199 | try test__powisf2(-0.0, -2, math.inf_f32); | ||
| 200 | try test__powisf2(-0.0, -3, -math.inf_f32); | ||
| 201 | try test__powisf2(-0.0, -4, math.inf_f32); | ||
| 202 | try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f32); | ||
| 203 | try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f32); | ||
| 204 | try test__powisf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f32); | ||
| 205 | |||
| 206 | try test__powisf2(1, -1, 1); | ||
| 207 | try test__powisf2(1, -2, 1); | ||
| 208 | try test__powisf2(1, -3, 1); | ||
| 209 | try test__powisf2(1, -4, 1); | ||
| 210 | try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1); | ||
| 211 | try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1); | ||
| 212 | try test__powisf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1); | ||
| 213 | |||
| 214 | try test__powisf2(math.inf_f32, -1, 0); | ||
| 215 | try test__powisf2(math.inf_f32, -2, 0); | ||
| 216 | try test__powisf2(math.inf_f32, -3, 0); | ||
| 217 | try test__powisf2(math.inf_f32, -4, 0); | ||
| 218 | try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 219 | try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000001)), 0); | ||
| 220 | try test__powisf2(math.inf_f32, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 221 | |||
| 222 | try test__powisf2(-math.inf_f32, -1, -0.0); | ||
| 223 | try test__powisf2(-math.inf_f32, -2, 0); | ||
| 224 | try test__powisf2(-math.inf_f32, -3, -0.0); | ||
| 225 | try test__powisf2(-math.inf_f32, -4, 0); | ||
| 226 | try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 227 | try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000001)), -0.0); | ||
| 228 | try test__powisf2(-math.inf_f32, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 229 | |||
| 230 | try test__powisf2(2.0, 10, 1024.0); | ||
| 231 | try test__powisf2(-2, 10, 1024.0); | ||
| 232 | try test__powisf2(2, -10, 1.0 / 1024.0); | ||
| 233 | try test__powisf2(-2, -10, 1.0 / 1024.0); | ||
| 234 | // | ||
| 235 | try test__powisf2(2, 19, 524288.0); | ||
| 236 | try test__powisf2(-2, 19, -524288.0); | ||
| 237 | try test__powisf2(2, -19, 1.0 / 524288.0); | ||
| 238 | try test__powisf2(-2, -19, -1.0 / 524288.0); | ||
| 239 | |||
| 240 | try test__powisf2(2, 31, 2147483648.0); | ||
| 241 | try test__powisf2(-2, 31, -2147483648.0); | ||
| 242 | try test__powisf2(2, -31, 1.0 / 2147483648.0); | ||
| 243 | try test__powisf2(-2, -31, -1.0 / 2147483648.0); | ||
| 244 | } | ||
| 245 | |||
| 246 | test "powidf2" { | ||
| 247 | try test__powidf2(0, 0, 1); | ||
| 248 | try test__powidf2(1, 0, 1); | ||
| 249 | try test__powidf2(1.5, 0, 1); | ||
| 250 | try test__powidf2(2, 0, 1); | ||
| 251 | try test__powidf2(math.inf_f64, 0, 1); | ||
| 252 | |||
| 253 | try test__powidf2(-0.0, 0, 1); | ||
| 254 | try test__powidf2(-1, 0, 1); | ||
| 255 | try test__powidf2(-1.5, 0, 1); | ||
| 256 | try test__powidf2(-2, 0, 1); | ||
| 257 | try test__powidf2(-math.inf_f64, 0, 1); | ||
| 258 | |||
| 259 | try test__powidf2(0, 1, 0); | ||
| 260 | try test__powidf2(0, 2, 0); | ||
| 261 | try test__powidf2(0, 3, 0); | ||
| 262 | try test__powidf2(0, 4, 0); | ||
| 263 | try test__powidf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 264 | try test__powidf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0); | ||
| 265 | |||
| 266 | try test__powidf2(-0.0, 1, -0.0); | ||
| 267 | try test__powidf2(-0.0, 2, 0); | ||
| 268 | try test__powidf2(-0.0, 3, -0.0); | ||
| 269 | try test__powidf2(-0.0, 4, 0); | ||
| 270 | try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 271 | try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0); | ||
| 272 | |||
| 273 | try test__powidf2(1, 1, 1); | ||
| 274 | try test__powidf2(1, 2, 1); | ||
| 275 | try test__powidf2(1, 3, 1); | ||
| 276 | try test__powidf2(1, 4, 1); | ||
| 277 | try test__powidf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1); | ||
| 278 | try test__powidf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1); | ||
| 279 | |||
| 280 | try test__powidf2(math.inf_f64, 1, math.inf_f64); | ||
| 281 | try test__powidf2(math.inf_f64, 2, math.inf_f64); | ||
| 282 | try test__powidf2(math.inf_f64, 3, math.inf_f64); | ||
| 283 | try test__powidf2(math.inf_f64, 4, math.inf_f64); | ||
| 284 | try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f64); | ||
| 285 | try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f64); | ||
| 286 | |||
| 287 | try test__powidf2(-math.inf_f64, 1, -math.inf_f64); | ||
| 288 | try test__powidf2(-math.inf_f64, 2, math.inf_f64); | ||
| 289 | try test__powidf2(-math.inf_f64, 3, -math.inf_f64); | ||
| 290 | try test__powidf2(-math.inf_f64, 4, math.inf_f64); | ||
| 291 | try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f64); | ||
| 292 | try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f64); | ||
| 293 | |||
| 294 | try test__powidf2(0, -1, math.inf_f64); | ||
| 295 | try test__powidf2(0, -2, math.inf_f64); | ||
| 296 | try test__powidf2(0, -3, math.inf_f64); | ||
| 297 | try test__powidf2(0, -4, math.inf_f64); | ||
| 298 | try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f64); | ||
| 299 | try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f64); | ||
| 300 | try test__powidf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f64); | ||
| 301 | |||
| 302 | try test__powidf2(-0.0, -1, -math.inf_f64); | ||
| 303 | try test__powidf2(-0.0, -2, math.inf_f64); | ||
| 304 | try test__powidf2(-0.0, -3, -math.inf_f64); | ||
| 305 | try test__powidf2(-0.0, -4, math.inf_f64); | ||
| 306 | try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f64); | ||
| 307 | try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f64); | ||
| 308 | try test__powidf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f64); | ||
| 309 | |||
| 310 | try test__powidf2(1, -1, 1); | ||
| 311 | try test__powidf2(1, -2, 1); | ||
| 312 | try test__powidf2(1, -3, 1); | ||
| 313 | try test__powidf2(1, -4, 1); | ||
| 314 | try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1); | ||
| 315 | try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1); | ||
| 316 | try test__powidf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1); | ||
| 317 | |||
| 318 | try test__powidf2(math.inf_f64, -1, 0); | ||
| 319 | try test__powidf2(math.inf_f64, -2, 0); | ||
| 320 | try test__powidf2(math.inf_f64, -3, 0); | ||
| 321 | try test__powidf2(math.inf_f64, -4, 0); | ||
| 322 | try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 323 | try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000001)), 0); | ||
| 324 | try test__powidf2(math.inf_f64, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 325 | |||
| 326 | try test__powidf2(-math.inf_f64, -1, -0.0); | ||
| 327 | try test__powidf2(-math.inf_f64, -2, 0); | ||
| 328 | try test__powidf2(-math.inf_f64, -3, -0.0); | ||
| 329 | try test__powidf2(-math.inf_f64, -4, 0); | ||
| 330 | try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 331 | try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000001)), -0.0); | ||
| 332 | try test__powidf2(-math.inf_f64, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 333 | |||
| 334 | try test__powidf2(2, 10, 1024.0); | ||
| 335 | try test__powidf2(-2, 10, 1024.0); | ||
| 336 | try test__powidf2(2, -10, 1.0 / 1024.0); | ||
| 337 | try test__powidf2(-2, -10, 1.0 / 1024.0); | ||
| 338 | |||
| 339 | try test__powidf2(2, 19, 524288.0); | ||
| 340 | try test__powidf2(-2, 19, -524288.0); | ||
| 341 | try test__powidf2(2, -19, 1.0 / 524288.0); | ||
| 342 | try test__powidf2(-2, -19, -1.0 / 524288.0); | ||
| 343 | |||
| 344 | try test__powidf2(2, 31, 2147483648.0); | ||
| 345 | try test__powidf2(-2, 31, -2147483648.0); | ||
| 346 | try test__powidf2(2, -31, 1.0 / 2147483648.0); | ||
| 347 | try test__powidf2(-2, -31, -1.0 / 2147483648.0); | ||
| 348 | } | ||
| 349 | |||
| 350 | test "powitf2" { | ||
| 351 | try test__powitf2(0, 0, 1); | ||
| 352 | try test__powitf2(1, 0, 1); | ||
| 353 | try test__powitf2(1.5, 0, 1); | ||
| 354 | try test__powitf2(2, 0, 1); | ||
| 355 | try test__powitf2(math.inf_f128, 0, 1); | ||
| 356 | |||
| 357 | try test__powitf2(-0.0, 0, 1); | ||
| 358 | try test__powitf2(-1, 0, 1); | ||
| 359 | try test__powitf2(-1.5, 0, 1); | ||
| 360 | try test__powitf2(-2, 0, 1); | ||
| 361 | try test__powitf2(-math.inf_f128, 0, 1); | ||
| 362 | |||
| 363 | try test__powitf2(0, 1, 0); | ||
| 364 | try test__powitf2(0, 2, 0); | ||
| 365 | try test__powitf2(0, 3, 0); | ||
| 366 | try test__powitf2(0, 4, 0); | ||
| 367 | try test__powitf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 368 | try test__powitf2(0, 0x7FFFFFFF, 0); | ||
| 369 | |||
| 370 | try test__powitf2(-0.0, 1, -0.0); | ||
| 371 | try test__powitf2(-0.0, 2, 0); | ||
| 372 | try test__powitf2(-0.0, 3, -0.0); | ||
| 373 | try test__powitf2(-0.0, 4, 0); | ||
| 374 | try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 375 | try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0); | ||
| 376 | |||
| 377 | try test__powitf2(1, 1, 1); | ||
| 378 | try test__powitf2(1, 2, 1); | ||
| 379 | try test__powitf2(1, 3, 1); | ||
| 380 | try test__powitf2(1, 4, 1); | ||
| 381 | try test__powitf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1); | ||
| 382 | try test__powitf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1); | ||
| 383 | |||
| 384 | try test__powitf2(math.inf_f128, 1, math.inf_f128); | ||
| 385 | try test__powitf2(math.inf_f128, 2, math.inf_f128); | ||
| 386 | try test__powitf2(math.inf_f128, 3, math.inf_f128); | ||
| 387 | try test__powitf2(math.inf_f128, 4, math.inf_f128); | ||
| 388 | try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f128); | ||
| 389 | try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f128); | ||
| 390 | |||
| 391 | try test__powitf2(-math.inf_f128, 1, -math.inf_f128); | ||
| 392 | try test__powitf2(-math.inf_f128, 2, math.inf_f128); | ||
| 393 | try test__powitf2(-math.inf_f128, 3, -math.inf_f128); | ||
| 394 | try test__powitf2(-math.inf_f128, 4, math.inf_f128); | ||
| 395 | try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f128); | ||
| 396 | try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f128); | ||
| 397 | |||
| 398 | try test__powitf2(0, -1, math.inf_f128); | ||
| 399 | try test__powitf2(0, -2, math.inf_f128); | ||
| 400 | try test__powitf2(0, -3, math.inf_f128); | ||
| 401 | try test__powitf2(0, -4, math.inf_f128); | ||
| 402 | try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f128); | ||
| 403 | try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f128); | ||
| 404 | try test__powitf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f128); | ||
| 405 | |||
| 406 | try test__powitf2(-0.0, -1, -math.inf_f128); | ||
| 407 | try test__powitf2(-0.0, -2, math.inf_f128); | ||
| 408 | try test__powitf2(-0.0, -3, -math.inf_f128); | ||
| 409 | try test__powitf2(-0.0, -4, math.inf_f128); | ||
| 410 | try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f128); | ||
| 411 | try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f128); | ||
| 412 | try test__powitf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f128); | ||
| 413 | |||
| 414 | try test__powitf2(1, -1, 1); | ||
| 415 | try test__powitf2(1, -2, 1); | ||
| 416 | try test__powitf2(1, -3, 1); | ||
| 417 | try test__powitf2(1, -4, 1); | ||
| 418 | try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1); | ||
| 419 | try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1); | ||
| 420 | try test__powitf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1); | ||
| 421 | |||
| 422 | try test__powitf2(math.inf_f128, -1, 0); | ||
| 423 | try test__powitf2(math.inf_f128, -2, 0); | ||
| 424 | try test__powitf2(math.inf_f128, -3, 0); | ||
| 425 | try test__powitf2(math.inf_f128, -4, 0); | ||
| 426 | try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 427 | try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000001)), 0); | ||
| 428 | try test__powitf2(math.inf_f128, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 429 | |||
| 430 | try test__powitf2(-math.inf_f128, -1, -0.0); | ||
| 431 | try test__powitf2(-math.inf_f128, -2, 0); | ||
| 432 | try test__powitf2(-math.inf_f128, -3, -0.0); | ||
| 433 | try test__powitf2(-math.inf_f128, -4, 0); | ||
| 434 | try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 435 | try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000001)), -0.0); | ||
| 436 | try test__powitf2(-math.inf_f128, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 437 | |||
| 438 | try test__powitf2(2, 10, 1024.0); | ||
| 439 | try test__powitf2(-2, 10, 1024.0); | ||
| 440 | try test__powitf2(2, -10, 1.0 / 1024.0); | ||
| 441 | try test__powitf2(-2, -10, 1.0 / 1024.0); | ||
| 442 | |||
| 443 | try test__powitf2(2, 19, 524288.0); | ||
| 444 | try test__powitf2(-2, 19, -524288.0); | ||
| 445 | try test__powitf2(2, -19, 1.0 / 524288.0); | ||
| 446 | try test__powitf2(-2, -19, -1.0 / 524288.0); | ||
| 447 | |||
| 448 | try test__powitf2(2, 31, 2147483648.0); | ||
| 449 | try test__powitf2(-2, 31, -2147483648.0); | ||
| 450 | try test__powitf2(2, -31, 1.0 / 2147483648.0); | ||
| 451 | try test__powitf2(-2, -31, -1.0 / 2147483648.0); | ||
| 452 | } | ||
| 453 | |||
| 454 | test "powixf2" { | ||
| 455 | try test__powixf2(0, 0, 1); | ||
| 456 | try test__powixf2(1, 0, 1); | ||
| 457 | try test__powixf2(1.5, 0, 1); | ||
| 458 | try test__powixf2(2, 0, 1); | ||
| 459 | try test__powixf2(math.inf_f80, 0, 1); | ||
| 460 | |||
| 461 | try test__powixf2(-0.0, 0, 1); | ||
| 462 | try test__powixf2(-1, 0, 1); | ||
| 463 | try test__powixf2(-1.5, 0, 1); | ||
| 464 | try test__powixf2(-2, 0, 1); | ||
| 465 | try test__powixf2(-math.inf_f80, 0, 1); | ||
| 466 | |||
| 467 | try test__powixf2(0, 1, 0); | ||
| 468 | try test__powixf2(0, 2, 0); | ||
| 469 | try test__powixf2(0, 3, 0); | ||
| 470 | try test__powixf2(0, 4, 0); | ||
| 471 | try test__powixf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 472 | try test__powixf2(0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 0); | ||
| 473 | |||
| 474 | try test__powixf2(-0.0, 1, -0.0); | ||
| 475 | try test__powixf2(-0.0, 2, 0); | ||
| 476 | try test__powixf2(-0.0, 3, -0.0); | ||
| 477 | try test__powixf2(-0.0, 4, 0); | ||
| 478 | try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 0); | ||
| 479 | try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -0.0); | ||
| 480 | |||
| 481 | try test__powixf2(1, 1, 1); | ||
| 482 | try test__powixf2(1, 2, 1); | ||
| 483 | try test__powixf2(1, 3, 1); | ||
| 484 | try test__powixf2(1, 4, 1); | ||
| 485 | try test__powixf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFE)), 1); | ||
| 486 | try test__powixf2(1, @bitCast(i32, @as(u32, 0x7FFFFFFF)), 1); | ||
| 487 | |||
| 488 | try test__powixf2(math.inf_f80, 1, math.inf_f80); | ||
| 489 | try test__powixf2(math.inf_f80, 2, math.inf_f80); | ||
| 490 | try test__powixf2(math.inf_f80, 3, math.inf_f80); | ||
| 491 | try test__powixf2(math.inf_f80, 4, math.inf_f80); | ||
| 492 | try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f80); | ||
| 493 | try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFF)), math.inf_f80); | ||
| 494 | |||
| 495 | try test__powixf2(-math.inf_f80, 1, -math.inf_f80); | ||
| 496 | try test__powixf2(-math.inf_f80, 2, math.inf_f80); | ||
| 497 | try test__powixf2(-math.inf_f80, 3, -math.inf_f80); | ||
| 498 | try test__powixf2(-math.inf_f80, 4, math.inf_f80); | ||
| 499 | try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFE)), math.inf_f80); | ||
| 500 | try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x7FFFFFFF)), -math.inf_f80); | ||
| 501 | |||
| 502 | try test__powixf2(0, -1, math.inf_f80); | ||
| 503 | try test__powixf2(0, -2, math.inf_f80); | ||
| 504 | try test__powixf2(0, -3, math.inf_f80); | ||
| 505 | try test__powixf2(0, -4, math.inf_f80); | ||
| 506 | try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f80); | ||
| 507 | try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000001)), math.inf_f80); | ||
| 508 | try test__powixf2(0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f80); | ||
| 509 | |||
| 510 | try test__powixf2(-0.0, -1, -math.inf_f80); | ||
| 511 | try test__powixf2(-0.0, -2, math.inf_f80); | ||
| 512 | try test__powixf2(-0.0, -3, -math.inf_f80); | ||
| 513 | try test__powixf2(-0.0, -4, math.inf_f80); | ||
| 514 | try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000002)), math.inf_f80); | ||
| 515 | try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000001)), -math.inf_f80); | ||
| 516 | try test__powixf2(-0.0, @bitCast(i32, @as(u32, 0x80000000)), math.inf_f80); | ||
| 517 | |||
| 518 | try test__powixf2(1, -1, 1); | ||
| 519 | try test__powixf2(1, -2, 1); | ||
| 520 | try test__powixf2(1, -3, 1); | ||
| 521 | try test__powixf2(1, -4, 1); | ||
| 522 | try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000002)), 1); | ||
| 523 | try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000001)), 1); | ||
| 524 | try test__powixf2(1, @bitCast(i32, @as(u32, 0x80000000)), 1); | ||
| 525 | |||
| 526 | try test__powixf2(math.inf_f80, -1, 0); | ||
| 527 | try test__powixf2(math.inf_f80, -2, 0); | ||
| 528 | try test__powixf2(math.inf_f80, -3, 0); | ||
| 529 | try test__powixf2(math.inf_f80, -4, 0); | ||
| 530 | try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 531 | try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000001)), 0); | ||
| 532 | try test__powixf2(math.inf_f80, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 533 | |||
| 534 | try test__powixf2(-math.inf_f80, -1, -0.0); | ||
| 535 | try test__powixf2(-math.inf_f80, -2, 0); | ||
| 536 | try test__powixf2(-math.inf_f80, -3, -0.0); | ||
| 537 | try test__powixf2(-math.inf_f80, -4, 0); | ||
| 538 | try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000002)), 0); | ||
| 539 | try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000001)), -0.0); | ||
| 540 | try test__powixf2(-math.inf_f80, @bitCast(i32, @as(u32, 0x80000000)), 0); | ||
| 541 | |||
| 542 | try test__powixf2(2, 10, 1024.0); | ||
| 543 | try test__powixf2(-2, 10, 1024.0); | ||
| 544 | try test__powixf2(2, -10, 1.0 / 1024.0); | ||
| 545 | try test__powixf2(-2, -10, 1.0 / 1024.0); | ||
| 546 | |||
| 547 | try test__powixf2(2, 19, 524288.0); | ||
| 548 | try test__powixf2(-2, 19, -524288.0); | ||
| 549 | try test__powixf2(2, -19, 1.0 / 524288.0); | ||
| 550 | try test__powixf2(-2, -19, -1.0 / 524288.0); | ||
| 551 | |||
| 552 | try test__powixf2(2, 31, 2147483648.0); | ||
| 553 | try test__powixf2(-2, 31, -2147483648.0); | ||
| 554 | try test__powixf2(2, -31, 1.0 / 2147483648.0); | ||
| 555 | try test__powixf2(-2, -31, -1.0 / 2147483648.0); | ||
| 556 | } | ||
lib/compiler_rt/shift.zig+21-11| ... | @@ -7,6 +7,11 @@ const common = @import("common.zig"); | ... | @@ -7,6 +7,11 @@ const common = @import("common.zig"); |
| 7 | pub const panic = common.panic; | 7 | pub const panic = common.panic; |
| 8 | 8 | ||
| 9 | comptime { | 9 | comptime { |
| 10 | // symbol compatibility with libgcc | ||
| 11 | @export(__ashlsi3, .{ .name = "__ashlsi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 12 | @export(__ashrsi3, .{ .name = "__ashrsi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 13 | @export(__lshrsi3, .{ .name = "__lshrsi3", .linkage = common.linkage, .visibility = common.visibility }); | ||
| 14 | |||
| 10 | @export(__ashlti3, .{ .name = "__ashlti3", .linkage = common.linkage, .visibility = common.visibility }); | 15 | @export(__ashlti3, .{ .name = "__ashlti3", .linkage = common.linkage, .visibility = common.visibility }); |
| 11 | @export(__ashrti3, .{ .name = "__ashrti3", .linkage = common.linkage, .visibility = common.visibility }); | 16 | @export(__ashrti3, .{ .name = "__ashrti3", .linkage = common.linkage, .visibility = common.visibility }); |
| 12 | @export(__lshrti3, .{ .name = "__lshrti3", .linkage = common.linkage, .visibility = common.visibility }); | 17 | @export(__lshrti3, .{ .name = "__lshrti3", .linkage = common.linkage, .visibility = common.visibility }); |
| ... | @@ -37,7 +42,7 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type { | ... | @@ -37,7 +42,7 @@ fn Dwords(comptime T: type, comptime signed_half: bool) type { |
| 37 | }; | 42 | }; |
| 38 | } | 43 | } |
| 39 | 44 | ||
| 40 | // Arithmetic shift left | 45 | // Arithmetic shift left: shift in 0 from right to left |
| 41 | // Precondition: 0 <= b < bits_in_dword | 46 | // Precondition: 0 <= b < bits_in_dword |
| 42 | inline fn ashlXi3(comptime T: type, a: T, b: i32) T { | 47 | inline fn ashlXi3(comptime T: type, a: T, b: i32) T { |
| 43 | const dwords = Dwords(T, false); | 48 | const dwords = Dwords(T, false); |
| ... | @@ -60,7 +65,7 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T { | ... | @@ -60,7 +65,7 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T { |
| 60 | return output.all; | 65 | return output.all; |
| 61 | } | 66 | } |
| 62 | 67 | ||
| 63 | // Arithmetic shift right | 68 | // Arithmetic shift right: shift in 1 from left to right |
| 64 | // Precondition: 0 <= b < T.bit_count | 69 | // Precondition: 0 <= b < T.bit_count |
| 65 | inline fn ashrXi3(comptime T: type, a: T, b: i32) T { | 70 | inline fn ashrXi3(comptime T: type, a: T, b: i32) T { |
| 66 | const dwords = Dwords(T, true); | 71 | const dwords = Dwords(T, true); |
| ... | @@ -87,7 +92,7 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T { | ... | @@ -87,7 +92,7 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T { |
| 87 | return output.all; | 92 | return output.all; |
| 88 | } | 93 | } |
| 89 | 94 | ||
| 90 | // Logical shift right | 95 | // Logical shift right: shift in 0 from left to right |
| 91 | // Precondition: 0 <= b < T.bit_count | 96 | // Precondition: 0 <= b < T.bit_count |
| 92 | inline fn lshrXi3(comptime T: type, a: T, b: i32) T { | 97 | inline fn lshrXi3(comptime T: type, a: T, b: i32) T { |
| 93 | const dwords = Dwords(T, false); | 98 | const dwords = Dwords(T, false); |
| ... | @@ -110,6 +115,18 @@ inline fn lshrXi3(comptime T: type, a: T, b: i32) T { | ... | @@ -110,6 +115,18 @@ inline fn lshrXi3(comptime T: type, a: T, b: i32) T { |
| 110 | return output.all; | 115 | return output.all; |
| 111 | } | 116 | } |
| 112 | 117 | ||
| 118 | pub fn __ashlsi3(a: i32, b: i32) callconv(.C) i32 { | ||
| 119 | return ashlXi3(i32, a, b); | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn __ashrsi3(a: i32, b: i32) callconv(.C) i32 { | ||
| 123 | return ashrXi3(i32, a, b); | ||
| 124 | } | ||
| 125 | |||
| 126 | pub fn __lshrsi3(a: i32, b: i32) callconv(.C) i32 { | ||
| 127 | return lshrXi3(i32, a, b); | ||
| 128 | } | ||
| 129 | |||
| 113 | pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 { | 130 | pub fn __ashldi3(a: i64, b: i32) callconv(.C) i64 { |
| 114 | return ashlXi3(i64, a, b); | 131 | return ashlXi3(i64, a, b); |
| 115 | } | 132 | } |
| ... | @@ -144,12 +161,5 @@ pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { | ... | @@ -144,12 +161,5 @@ pub fn __lshrti3(a: i128, b: i32) callconv(.C) i128 { |
| 144 | } | 161 | } |
| 145 | 162 | ||
| 146 | test { | 163 | test { |
| 147 | _ = @import("ashrdi3_test.zig"); | 164 | _ = @import("shift_test.zig"); |
| 148 | _ = @import("ashrti3_test.zig"); | ||
| 149 | |||
| 150 | _ = @import("ashldi3_test.zig"); | ||
| 151 | _ = @import("ashlti3_test.zig"); | ||
| 152 | |||
| 153 | _ = @import("lshrdi3_test.zig"); | ||
| 154 | _ = @import("lshrti3_test.zig"); | ||
| 155 | } | 165 | } |
lib/compiler_rt/shift_test.zig created+363| ... | @@ -0,0 +1,363 @@ | ||
| 1 | const testing = @import("std").testing; | ||
| 2 | const shift = @import("shift.zig"); | ||
| 3 | |||
| 4 | // arithmetic shift left | ||
| 5 | const __ashlsi3 = shift.__ashlsi3; | ||
| 6 | const __ashldi3 = shift.__ashldi3; | ||
| 7 | const __ashlti3 = shift.__ashlti3; | ||
| 8 | |||
| 9 | // arithmetic shift right | ||
| 10 | const __ashrsi3 = shift.__ashrsi3; | ||
| 11 | const __ashrdi3 = shift.__ashrdi3; | ||
| 12 | const __ashrti3 = shift.__ashrti3; | ||
| 13 | |||
| 14 | // logic shift right | ||
| 15 | const __lshrsi3 = shift.__lshrsi3; | ||
| 16 | const __lshrdi3 = shift.__lshrdi3; | ||
| 17 | const __lshrti3 = shift.__lshrti3; | ||
| 18 | |||
| 19 | fn test__ashlsi3(a: i32, b: i32, expected: u32) !void { | ||
| 20 | const x = __ashlsi3(a, b); | ||
| 21 | try testing.expectEqual(expected, @bitCast(u32, x)); | ||
| 22 | } | ||
| 23 | fn test__ashldi3(a: i64, b: i32, expected: u64) !void { | ||
| 24 | const x = __ashldi3(a, b); | ||
| 25 | try testing.expectEqual(expected, @bitCast(u64, x)); | ||
| 26 | } | ||
| 27 | fn test__ashlti3(a: i128, b: i32, expected: u128) !void { | ||
| 28 | const x = __ashlti3(a, b); | ||
| 29 | try testing.expectEqual(expected, @bitCast(u128, x)); | ||
| 30 | } | ||
| 31 | |||
| 32 | test "ashlsi3" { | ||
| 33 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 0, 0x12ABCDEF); | ||
| 34 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 1, 0x25579BDE); | ||
| 35 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 2, 0x4AAF37BC); | ||
| 36 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 3, 0x955E6F78); | ||
| 37 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 4, 0x2ABCDEF0); | ||
| 38 | |||
| 39 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 28, 0xF0000000); | ||
| 40 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 29, 0xE0000000); | ||
| 41 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 30, 0xC0000000); | ||
| 42 | try test__ashlsi3(@bitCast(i32, @as(u32, 0x12ABCDEF)), 31, 0x80000000); | ||
| 43 | } | ||
| 44 | |||
| 45 | test "ashldi3" { | ||
| 46 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF); | ||
| 47 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x2468ACF13579BDE); | ||
| 48 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37BC); | ||
| 49 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x91A2B3C4D5E6F78); | ||
| 50 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDEF0); | ||
| 51 | |||
| 52 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x789ABCDEF0000000); | ||
| 53 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0xF13579BDE0000000); | ||
| 54 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0xE26AF37BC0000000); | ||
| 55 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0xC4D5E6F780000000); | ||
| 56 | |||
| 57 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x89ABCDEF00000000); | ||
| 58 | |||
| 59 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x13579BDE00000000); | ||
| 60 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x26AF37BC00000000); | ||
| 61 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x4D5E6F7800000000); | ||
| 62 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x9ABCDEF000000000); | ||
| 63 | |||
| 64 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0xF000000000000000); | ||
| 65 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0xE000000000000000); | ||
| 66 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0xC000000000000000); | ||
| 67 | try test__ashldi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0x8000000000000000); | ||
| 68 | } | ||
| 69 | |||
| 70 | test "ashlti3" { | ||
| 71 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, 0xFEDCBA9876543215FEDCBA9876543215); | ||
| 72 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, 0xFDB97530ECA8642BFDB97530ECA8642A); | ||
| 73 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, 0xFB72EA61D950C857FB72EA61D950C854); | ||
| 74 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, 0xF6E5D4C3B2A190AFF6E5D4C3B2A190A8); | ||
| 75 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, 0xEDCBA9876543215FEDCBA98765432150); | ||
| 76 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, 0x876543215FEDCBA98765432150000000); | ||
| 77 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, 0x0ECA8642BFDB97530ECA8642A0000000); | ||
| 78 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, 0x1D950C857FB72EA61D950C8540000000); | ||
| 79 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, 0x3B2A190AFF6E5D4C3B2A190A80000000); | ||
| 80 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, 0x76543215FEDCBA987654321500000000); | ||
| 81 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, 0xECA8642BFDB97530ECA8642A00000000); | ||
| 82 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, 0xD950C857FB72EA61D950C85400000000); | ||
| 83 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, 0xB2A190AFF6E5D4C3B2A190A800000000); | ||
| 84 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, 0x6543215FEDCBA9876543215000000000); | ||
| 85 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, 0x5FEDCBA9876543215000000000000000); | ||
| 86 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, 0xBFDB97530ECA8642A000000000000000); | ||
| 87 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, 0x7FB72EA61D950C854000000000000000); | ||
| 88 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, 0xFF6E5D4C3B2A190A8000000000000000); | ||
| 89 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, 0xFEDCBA98765432150000000000000000); | ||
| 90 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, 0xFDB97530ECA8642A0000000000000000); | ||
| 91 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, 0xFB72EA61D950C8540000000000000000); | ||
| 92 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, 0xF6E5D4C3B2A190A80000000000000000); | ||
| 93 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, 0xEDCBA987654321500000000000000000); | ||
| 94 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, 0x87654321500000000000000000000000); | ||
| 95 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, 0x0ECA8642A00000000000000000000000); | ||
| 96 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, 0x1D950C85400000000000000000000000); | ||
| 97 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, 0x3B2A190A800000000000000000000000); | ||
| 98 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, 0x76543215000000000000000000000000); | ||
| 99 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, 0xECA8642A000000000000000000000000); | ||
| 100 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, 0xD950C854000000000000000000000000); | ||
| 101 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, 0xB2A190A8000000000000000000000000); | ||
| 102 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, 0x65432150000000000000000000000000); | ||
| 103 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, 0x50000000000000000000000000000000); | ||
| 104 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, 0xA0000000000000000000000000000000); | ||
| 105 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, 0x40000000000000000000000000000000); | ||
| 106 | try test__ashlti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, 0x80000000000000000000000000000000); | ||
| 107 | } | ||
| 108 | |||
| 109 | fn test__ashrsi3(a: i32, b: i32, expected: u32) !void { | ||
| 110 | const x = __ashrsi3(a, b); | ||
| 111 | try testing.expectEqual(expected, @bitCast(u32, x)); | ||
| 112 | } | ||
| 113 | fn test__ashrdi3(a: i64, b: i32, expected: u64) !void { | ||
| 114 | const x = __ashrdi3(a, b); | ||
| 115 | try testing.expectEqual(expected, @bitCast(u64, x)); | ||
| 116 | } | ||
| 117 | fn test__ashrti3(a: i128, b: i32, expected: u128) !void { | ||
| 118 | const x = __ashrti3(a, b); | ||
| 119 | try testing.expectEqual(expected, @bitCast(u128, x)); | ||
| 120 | } | ||
| 121 | |||
| 122 | test "ashrsi3" { | ||
| 123 | try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 0, 0xFEDBCA98); | ||
| 124 | try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 1, 0xFF6DE54C); | ||
| 125 | try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 2, 0xFFB6F2A6); | ||
| 126 | try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 3, 0xFFDB7953); | ||
| 127 | try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 4, 0xFFEDBCA9); | ||
| 128 | |||
| 129 | try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 28, 0xFFFFFFFF); | ||
| 130 | try test__ashrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 31, 0xFFFFFFFF); | ||
| 131 | |||
| 132 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 0, 0x8CEF8CEF); | ||
| 133 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 1, 0xC677C677); | ||
| 134 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 2, 0xE33BE33B); | ||
| 135 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 3, 0xF19DF19D); | ||
| 136 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 4, 0xF8CEF8CE); | ||
| 137 | |||
| 138 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 28, 0xFFFFFFF8); | ||
| 139 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 29, 0xFFFFFFFC); | ||
| 140 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 30, 0xFFFFFFFE); | ||
| 141 | try test__ashrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 31, 0xFFFFFFFF); | ||
| 142 | } | ||
| 143 | |||
| 144 | test "ashrdi3" { | ||
| 145 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF); | ||
| 146 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7); | ||
| 147 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B); | ||
| 148 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD); | ||
| 149 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE); | ||
| 150 | |||
| 151 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678); | ||
| 152 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C); | ||
| 153 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E); | ||
| 154 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF); | ||
| 155 | |||
| 156 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567); | ||
| 157 | |||
| 158 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3); | ||
| 159 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159); | ||
| 160 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC); | ||
| 161 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456); | ||
| 162 | |||
| 163 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0); | ||
| 164 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0); | ||
| 165 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0); | ||
| 166 | try test__ashrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0); | ||
| 167 | |||
| 168 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210); | ||
| 169 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0xFF6E5D4C3B2A1908); | ||
| 170 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0xFFB72EA61D950C84); | ||
| 171 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0xFFDB97530ECA8642); | ||
| 172 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFFEDCBA987654321); | ||
| 173 | |||
| 174 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFFFFFFFFEDCBA987); | ||
| 175 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0xFFFFFFFFF6E5D4C3); | ||
| 176 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0xFFFFFFFFFB72EA61); | ||
| 177 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0xFFFFFFFFFDB97530); | ||
| 178 | |||
| 179 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFFFFFFFFFEDCBA98); | ||
| 180 | |||
| 181 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0xFFFFFFFFFF6E5D4C); | ||
| 182 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0xFFFFFFFFFFB72EA6); | ||
| 183 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0xFFFFFFFFFFDB9753); | ||
| 184 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFFFFFFFFFFEDCBA9); | ||
| 185 | |||
| 186 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xFFFFFFFFFFFFFFFA); | ||
| 187 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0xFFFFFFFFFFFFFFFD); | ||
| 188 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0xFFFFFFFFFFFFFFFE); | ||
| 189 | try test__ashrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0xFFFFFFFFFFFFFFFF); | ||
| 190 | } | ||
| 191 | |||
| 192 | test "ashrti3" { | ||
| 193 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, 0xFEDCBA9876543215FEDCBA9876543215); | ||
| 194 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, 0xFF6E5D4C3B2A190AFF6E5D4C3B2A190A); | ||
| 195 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, 0xFFB72EA61D950C857FB72EA61D950C85); | ||
| 196 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, 0xFFDB97530ECA8642BFDB97530ECA8642); | ||
| 197 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, 0xFFEDCBA9876543215FEDCBA987654321); | ||
| 198 | |||
| 199 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, 0xFFFFFFFFEDCBA9876543215FEDCBA987); | ||
| 200 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, 0xFFFFFFFFF6E5D4C3B2A190AFF6E5D4C3); | ||
| 201 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, 0xFFFFFFFFFB72EA61D950C857FB72EA61); | ||
| 202 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, 0xFFFFFFFFFDB97530ECA8642BFDB97530); | ||
| 203 | |||
| 204 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, 0xFFFFFFFFFEDCBA9876543215FEDCBA98); | ||
| 205 | |||
| 206 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, 0xFFFFFFFFFF6E5D4C3B2A190AFF6E5D4C); | ||
| 207 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, 0xFFFFFFFFFFB72EA61D950C857FB72EA6); | ||
| 208 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, 0xFFFFFFFFFFDB97530ECA8642BFDB9753); | ||
| 209 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, 0xFFFFFFFFFFEDCBA9876543215FEDCBA9); | ||
| 210 | |||
| 211 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, 0xFFFFFFFFFFFFFFFFEDCBA9876543215F); | ||
| 212 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, 0xFFFFFFFFFFFFFFFFF6E5D4C3B2A190AF); | ||
| 213 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, 0xFFFFFFFFFFFFFFFFFB72EA61D950C857); | ||
| 214 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, 0xFFFFFFFFFFFFFFFFFDB97530ECA8642B); | ||
| 215 | |||
| 216 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, 0xFFFFFFFFFFFFFFFFFEDCBA9876543215); | ||
| 217 | |||
| 218 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, 0xFFFFFFFFFFFFFFFFFF6E5D4C3B2A190A); | ||
| 219 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, 0xFFFFFFFFFFFFFFFFFFB72EA61D950C85); | ||
| 220 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, 0xFFFFFFFFFFFFFFFFFFDB97530ECA8642); | ||
| 221 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, 0xFFFFFFFFFFFFFFFFFFEDCBA987654321); | ||
| 222 | |||
| 223 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, 0xFFFFFFFFFFFFFFFFFFFFFFFFEDCBA987); | ||
| 224 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, 0xFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C3); | ||
| 225 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, 0xFFFFFFFFFFFFFFFFFFFFFFFFFB72EA61); | ||
| 226 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, 0xFFFFFFFFFFFFFFFFFFFFFFFFFDB97530); | ||
| 227 | |||
| 228 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, 0xFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA98); | ||
| 229 | |||
| 230 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C); | ||
| 231 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFB72EA6); | ||
| 232 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFDB9753); | ||
| 233 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA9); | ||
| 234 | |||
| 235 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 236 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 237 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 238 | try test__ashrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 239 | } | ||
| 240 | |||
| 241 | fn test__lshrsi3(a: i32, b: i32, expected: u32) !void { | ||
| 242 | const x = __lshrsi3(a, b); | ||
| 243 | try testing.expectEqual(expected, @bitCast(u32, x)); | ||
| 244 | } | ||
| 245 | fn test__lshrdi3(a: i64, b: i32, expected: u64) !void { | ||
| 246 | const x = __lshrdi3(a, b); | ||
| 247 | try testing.expectEqual(expected, @bitCast(u64, x)); | ||
| 248 | } | ||
| 249 | fn test__lshrti3(a: i128, b: i32, expected: u128) !void { | ||
| 250 | const x = __lshrti3(a, b); | ||
| 251 | try testing.expectEqual(expected, @bitCast(u128, x)); | ||
| 252 | } | ||
| 253 | |||
| 254 | test "lshrsi3" { | ||
| 255 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 0, 0xFEDBCA98); | ||
| 256 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 1, 0x7F6DE54C); | ||
| 257 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 2, 0x3FB6F2A6); | ||
| 258 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 3, 0x1FDB7953); | ||
| 259 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 4, 0xFEDBCA9); | ||
| 260 | |||
| 261 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 28, 0xF); | ||
| 262 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 29, 0x7); | ||
| 263 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 30, 0x3); | ||
| 264 | try test__lshrsi3(@bitCast(i32, @as(u32, 0xFEDBCA98)), 31, 0x1); | ||
| 265 | |||
| 266 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 0, 0x8CEF8CEF); | ||
| 267 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 1, 0x4677C677); | ||
| 268 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 2, 0x233BE33B); | ||
| 269 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 3, 0x119DF19D); | ||
| 270 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 4, 0x8CEF8CE); | ||
| 271 | |||
| 272 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 28, 0x8); | ||
| 273 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 29, 0x4); | ||
| 274 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 30, 0x2); | ||
| 275 | try test__lshrsi3(@bitCast(i32, @as(u32, 0x8CEF8CEF)), 31, 0x1); | ||
| 276 | } | ||
| 277 | |||
| 278 | test "lshrdi3" { | ||
| 279 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 0, 0x123456789ABCDEF); | ||
| 280 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 1, 0x91A2B3C4D5E6F7); | ||
| 281 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 2, 0x48D159E26AF37B); | ||
| 282 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 3, 0x2468ACF13579BD); | ||
| 283 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 4, 0x123456789ABCDE); | ||
| 284 | |||
| 285 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 28, 0x12345678); | ||
| 286 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 29, 0x91A2B3C); | ||
| 287 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 30, 0x48D159E); | ||
| 288 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 31, 0x2468ACF); | ||
| 289 | |||
| 290 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 32, 0x1234567); | ||
| 291 | |||
| 292 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 33, 0x91A2B3); | ||
| 293 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 34, 0x48D159); | ||
| 294 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 35, 0x2468AC); | ||
| 295 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 36, 0x123456); | ||
| 296 | |||
| 297 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 60, 0); | ||
| 298 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 61, 0); | ||
| 299 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 62, 0); | ||
| 300 | try test__lshrdi3(@bitCast(i64, @as(u64, 0x0123456789ABCDEF)), 63, 0); | ||
| 301 | |||
| 302 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 0, 0xFEDCBA9876543210); | ||
| 303 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 1, 0x7F6E5D4C3B2A1908); | ||
| 304 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 2, 0x3FB72EA61D950C84); | ||
| 305 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 3, 0x1FDB97530ECA8642); | ||
| 306 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 4, 0xFEDCBA987654321); | ||
| 307 | |||
| 308 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 28, 0xFEDCBA987); | ||
| 309 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 29, 0x7F6E5D4C3); | ||
| 310 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 30, 0x3FB72EA61); | ||
| 311 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 31, 0x1FDB97530); | ||
| 312 | |||
| 313 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 32, 0xFEDCBA98); | ||
| 314 | |||
| 315 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 33, 0x7F6E5D4C); | ||
| 316 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 34, 0x3FB72EA6); | ||
| 317 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 35, 0x1FDB9753); | ||
| 318 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xFEDCBA9876543210)), 36, 0xFEDCBA9); | ||
| 319 | |||
| 320 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 60, 0xA); | ||
| 321 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 61, 0x5); | ||
| 322 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 62, 0x2); | ||
| 323 | try test__lshrdi3(@bitCast(i64, @as(u64, 0xAEDCBA9876543210)), 63, 0x1); | ||
| 324 | } | ||
| 325 | |||
| 326 | test "lshrti3" { | ||
| 327 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 0, 0xFEDCBA9876543215FEDCBA987654321F); | ||
| 328 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 1, 0x7F6E5D4C3B2A190AFF6E5D4C3B2A190F); | ||
| 329 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 2, 0x3FB72EA61D950C857FB72EA61D950C87); | ||
| 330 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 3, 0x1FDB97530ECA8642BFDB97530ECA8643); | ||
| 331 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 4, 0xFEDCBA9876543215FEDCBA987654321); | ||
| 332 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 28, 0xFEDCBA9876543215FEDCBA987); | ||
| 333 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 29, 0x7F6E5D4C3B2A190AFF6E5D4C3); | ||
| 334 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 30, 0x3FB72EA61D950C857FB72EA61); | ||
| 335 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 31, 0x1FDB97530ECA8642BFDB97530); | ||
| 336 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 32, 0xFEDCBA9876543215FEDCBA98); | ||
| 337 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 33, 0x7F6E5D4C3B2A190AFF6E5D4C); | ||
| 338 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 34, 0x3FB72EA61D950C857FB72EA6); | ||
| 339 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 35, 0x1FDB97530ECA8642BFDB9753); | ||
| 340 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 36, 0xFEDCBA9876543215FEDCBA9); | ||
| 341 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 60, 0xFEDCBA9876543215F); | ||
| 342 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 61, 0x7F6E5D4C3B2A190AF); | ||
| 343 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 62, 0x3FB72EA61D950C857); | ||
| 344 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 63, 0x1FDB97530ECA8642B); | ||
| 345 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 64, 0xFEDCBA9876543215); | ||
| 346 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 65, 0x7F6E5D4C3B2A190A); | ||
| 347 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 66, 0x3FB72EA61D950C85); | ||
| 348 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 67, 0x1FDB97530ECA8642); | ||
| 349 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 68, 0xFEDCBA987654321); | ||
| 350 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 92, 0xFEDCBA987); | ||
| 351 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 93, 0x7F6E5D4C3); | ||
| 352 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 94, 0x3FB72EA61); | ||
| 353 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 95, 0x1FDB97530); | ||
| 354 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 96, 0xFEDCBA98); | ||
| 355 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 97, 0x7F6E5D4C); | ||
| 356 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 98, 0x3FB72EA6); | ||
| 357 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 99, 0x1FDB9753); | ||
| 358 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 100, 0xFEDCBA9); | ||
| 359 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 124, 0xF); | ||
| 360 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 125, 0x7); | ||
| 361 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 126, 0x3); | ||
| 362 | try test__lshrti3(@bitCast(i128, @as(u128, 0xFEDCBA9876543215FEDCBA987654321F)), 127, 0x1); | ||
| 363 | } | ||
lib/docs/main.js+6-2| ... | @@ -1354,8 +1354,12 @@ const NAV_MODES = { | ... | @@ -1354,8 +1354,12 @@ const NAV_MODES = { |
| 1354 | payloadHtml += "ptrCast"; | 1354 | payloadHtml += "ptrCast"; |
| 1355 | break; | 1355 | break; |
| 1356 | } | 1356 | } |
| 1357 | case "qual_cast": { | 1357 | case "const_cast": { |
| 1358 | payloadHtml += "qualCast"; | 1358 | payloadHtml += "constCast"; |
| 1359 | break; | ||
| 1360 | } | ||
| 1361 | case "volatile_cast": { | ||
| 1362 | payloadHtml += "volatileCast"; | ||
| 1359 | break; | 1363 | break; |
| 1360 | } | 1364 | } |
| 1361 | case "truncate": { | 1365 | case "truncate": { |
lib/std/Build.zig+65-53| ... | @@ -19,6 +19,8 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo; | ... | @@ -19,6 +19,8 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo; |
| 19 | const Sha256 = std.crypto.hash.sha2.Sha256; | 19 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 20 | const Build = @This(); | 20 | const Build = @This(); |
| 21 | 21 | ||
| 22 | pub const Cache = @import("Build/Cache.zig"); | ||
| 23 | |||
| 22 | /// deprecated: use `CompileStep`. | 24 | /// deprecated: use `CompileStep`. |
| 23 | pub const LibExeObjStep = CompileStep; | 25 | pub const LibExeObjStep = CompileStep; |
| 24 | /// deprecated: use `Build`. | 26 | /// deprecated: use `Build`. |
| ... | @@ -77,11 +79,12 @@ search_prefixes: ArrayList([]const u8), | ... | @@ -77,11 +79,12 @@ search_prefixes: ArrayList([]const u8), |
| 77 | libc_file: ?[]const u8 = null, | 79 | libc_file: ?[]const u8 = null, |
| 78 | installed_files: ArrayList(InstalledFile), | 80 | installed_files: ArrayList(InstalledFile), |
| 79 | /// Path to the directory containing build.zig. | 81 | /// Path to the directory containing build.zig. |
| 80 | build_root: []const u8, | 82 | build_root: Cache.Directory, |
| 81 | cache_root: []const u8, | 83 | cache_root: Cache.Directory, |
| 82 | global_cache_root: []const u8, | 84 | global_cache_root: Cache.Directory, |
| 83 | /// zig lib dir | 85 | cache: *Cache, |
| 84 | override_lib_dir: ?[]const u8, | 86 | /// If non-null, overrides the default zig lib dir. |
| 87 | zig_lib_dir: ?[]const u8, | ||
| 85 | vcpkg_root: VcpkgRoot = .unattempted, | 88 | vcpkg_root: VcpkgRoot = .unattempted, |
| 86 | pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, | 89 | pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, |
| 87 | args: ?[][]const u8 = null, | 90 | args: ?[][]const u8 = null, |
| ... | @@ -185,10 +188,11 @@ pub const DirList = struct { | ... | @@ -185,10 +188,11 @@ pub const DirList = struct { |
| 185 | pub fn create( | 188 | pub fn create( |
| 186 | allocator: Allocator, | 189 | allocator: Allocator, |
| 187 | zig_exe: []const u8, | 190 | zig_exe: []const u8, |
| 188 | build_root: []const u8, | 191 | build_root: Cache.Directory, |
| 189 | cache_root: []const u8, | 192 | cache_root: Cache.Directory, |
| 190 | global_cache_root: []const u8, | 193 | global_cache_root: Cache.Directory, |
| 191 | host: NativeTargetInfo, | 194 | host: NativeTargetInfo, |
| 195 | cache: *Cache, | ||
| 192 | ) !*Build { | 196 | ) !*Build { |
| 193 | const env_map = try allocator.create(EnvMap); | 197 | const env_map = try allocator.create(EnvMap); |
| 194 | env_map.* = try process.getEnvMap(allocator); | 198 | env_map.* = try process.getEnvMap(allocator); |
| ... | @@ -197,8 +201,9 @@ pub fn create( | ... | @@ -197,8 +201,9 @@ pub fn create( |
| 197 | self.* = Build{ | 201 | self.* = Build{ |
| 198 | .zig_exe = zig_exe, | 202 | .zig_exe = zig_exe, |
| 199 | .build_root = build_root, | 203 | .build_root = build_root, |
| 200 | .cache_root = try fs.path.relative(allocator, build_root, cache_root), | 204 | .cache_root = cache_root, |
| 201 | .global_cache_root = global_cache_root, | 205 | .global_cache_root = global_cache_root, |
| 206 | .cache = cache, | ||
| 202 | .verbose = false, | 207 | .verbose = false, |
| 203 | .verbose_link = false, | 208 | .verbose_link = false, |
| 204 | .verbose_cc = false, | 209 | .verbose_cc = false, |
| ... | @@ -230,7 +235,7 @@ pub fn create( | ... | @@ -230,7 +235,7 @@ pub fn create( |
| 230 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | 235 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), |
| 231 | .description = "Remove build artifacts from prefix path", | 236 | .description = "Remove build artifacts from prefix path", |
| 232 | }, | 237 | }, |
| 233 | .override_lib_dir = null, | 238 | .zig_lib_dir = null, |
| 234 | .install_path = undefined, | 239 | .install_path = undefined, |
| 235 | .args = null, | 240 | .args = null, |
| 236 | .host = host, | 241 | .host = host, |
| ... | @@ -245,7 +250,7 @@ pub fn create( | ... | @@ -245,7 +250,7 @@ pub fn create( |
| 245 | fn createChild( | 250 | fn createChild( |
| 246 | parent: *Build, | 251 | parent: *Build, |
| 247 | dep_name: []const u8, | 252 | dep_name: []const u8, |
| 248 | build_root: []const u8, | 253 | build_root: Cache.Directory, |
| 249 | args: anytype, | 254 | args: anytype, |
| 250 | ) !*Build { | 255 | ) !*Build { |
| 251 | const child = try createChildOnly(parent, dep_name, build_root); | 256 | const child = try createChildOnly(parent, dep_name, build_root); |
| ... | @@ -253,7 +258,7 @@ fn createChild( | ... | @@ -253,7 +258,7 @@ fn createChild( |
| 253 | return child; | 258 | return child; |
| 254 | } | 259 | } |
| 255 | 260 | ||
| 256 | fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build { | 261 | fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: Cache.Directory) !*Build { |
| 257 | const allocator = parent.allocator; | 262 | const allocator = parent.allocator; |
| 258 | const child = try allocator.create(Build); | 263 | const child = try allocator.create(Build); |
| 259 | child.* = .{ | 264 | child.* = .{ |
| ... | @@ -297,7 +302,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) | ... | @@ -297,7 +302,8 @@ fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) |
| 297 | .build_root = build_root, | 302 | .build_root = build_root, |
| 298 | .cache_root = parent.cache_root, | 303 | .cache_root = parent.cache_root, |
| 299 | .global_cache_root = parent.global_cache_root, | 304 | .global_cache_root = parent.global_cache_root, |
| 300 | .override_lib_dir = parent.override_lib_dir, | 305 | .cache = parent.cache, |
| 306 | .zig_lib_dir = parent.zig_lib_dir, | ||
| 301 | .debug_log_scopes = parent.debug_log_scopes, | 307 | .debug_log_scopes = parent.debug_log_scopes, |
| 302 | .debug_compile_errors = parent.debug_compile_errors, | 308 | .debug_compile_errors = parent.debug_compile_errors, |
| 303 | .enable_darling = parent.enable_darling, | 309 | .enable_darling = parent.enable_darling, |
| ... | @@ -348,7 +354,7 @@ fn applyArgs(b: *Build, args: anytype) !void { | ... | @@ -348,7 +354,7 @@ fn applyArgs(b: *Build, args: anytype) !void { |
| 348 | .used = false, | 354 | .used = false, |
| 349 | }); | 355 | }); |
| 350 | }, | 356 | }, |
| 351 | .Enum => { | 357 | .Enum, .EnumLiteral => { |
| 352 | try b.user_input_options.put(field.name, .{ | 358 | try b.user_input_options.put(field.name, .{ |
| 353 | .name = field.name, | 359 | .name = field.name, |
| 354 | .value = .{ .scalar = @tagName(v) }, | 360 | .value = .{ .scalar = @tagName(v) }, |
| ... | @@ -379,7 +385,7 @@ fn applyArgs(b: *Build, args: anytype) !void { | ... | @@ -379,7 +385,7 @@ fn applyArgs(b: *Build, args: anytype) !void { |
| 379 | _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch | 385 | _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch |
| 380 | unreachable; | 386 | unreachable; |
| 381 | 387 | ||
| 382 | const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename }); | 388 | const install_prefix = try b.cache_root.join(b.allocator, &.{ "i", &hash_basename }); |
| 383 | b.resolveInstallPrefix(install_prefix, .{}); | 389 | b.resolveInstallPrefix(install_prefix, .{}); |
| 384 | } | 390 | } |
| 385 | 391 | ||
| ... | @@ -396,7 +402,7 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: | ... | @@ -396,7 +402,7 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: |
| 396 | self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix }); | 402 | self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix }); |
| 397 | } else { | 403 | } else { |
| 398 | self.install_prefix = install_prefix orelse | 404 | self.install_prefix = install_prefix orelse |
| 399 | (self.pathJoin(&.{ self.build_root, "zig-out" })); | 405 | (self.build_root.join(self.allocator, &.{"zig-out"}) catch @panic("unhandled error")); |
| 400 | self.install_path = self.install_prefix; | 406 | self.install_path = self.install_prefix; |
| 401 | } | 407 | } |
| 402 | 408 | ||
| ... | @@ -535,6 +541,7 @@ pub const AssemblyOptions = struct { | ... | @@ -535,6 +541,7 @@ pub const AssemblyOptions = struct { |
| 535 | pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { | 541 | pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { |
| 536 | const obj_step = CompileStep.create(b, .{ | 542 | const obj_step = CompileStep.create(b, .{ |
| 537 | .name = options.name, | 543 | .name = options.name, |
| 544 | .kind = .obj, | ||
| 538 | .root_source_file = null, | 545 | .root_source_file = null, |
| 539 | .target = options.target, | 546 | .target = options.target, |
| 540 | .optimize = options.optimize, | 547 | .optimize = options.optimize, |
| ... | @@ -598,13 +605,39 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { | ... | @@ -598,13 +605,39 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { |
| 598 | return run_step; | 605 | return run_step; |
| 599 | } | 606 | } |
| 600 | 607 | ||
| 608 | /// Creates a `RunStep` with an executable built with `addExecutable`. | ||
| 609 | /// Add command line arguments with methods of `RunStep`. | ||
| 610 | pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { | ||
| 611 | assert(exe.kind == .exe or exe.kind == .test_exe); | ||
| 612 | |||
| 613 | // It doesn't have to be native. We catch that if you actually try to run it. | ||
| 614 | // Consider that this is declarative; the run step may not be run unless a user | ||
| 615 | // option is supplied. | ||
| 616 | const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.step.name})); | ||
| 617 | run_step.addArtifactArg(exe); | ||
| 618 | |||
| 619 | if (exe.kind == .test_exe) { | ||
| 620 | run_step.addArg(b.zig_exe); | ||
| 621 | } | ||
| 622 | |||
| 623 | if (exe.vcpkg_bin_path) |path| { | ||
| 624 | run_step.addPathDir(path); | ||
| 625 | } | ||
| 626 | |||
| 627 | return run_step; | ||
| 628 | } | ||
| 629 | |||
| 630 | /// Using the `values` provided, produces a C header file, possibly based on a | ||
| 631 | /// template input file (e.g. config.h.in). | ||
| 632 | /// When an input template file is provided, this function will fail the build | ||
| 633 | /// when an option not found in the input file is provided in `values`, and | ||
| 634 | /// when an option found in the input file is missing from `values`. | ||
| 601 | pub fn addConfigHeader( | 635 | pub fn addConfigHeader( |
| 602 | b: *Build, | 636 | b: *Build, |
| 603 | source: FileSource, | 637 | options: ConfigHeaderStep.Options, |
| 604 | style: ConfigHeaderStep.Style, | ||
| 605 | values: anytype, | 638 | values: anytype, |
| 606 | ) *ConfigHeaderStep { | 639 | ) *ConfigHeaderStep { |
| 607 | const config_header_step = ConfigHeaderStep.create(b, source, style); | 640 | const config_header_step = ConfigHeaderStep.create(b, options); |
| 608 | config_header_step.addValues(values); | 641 | config_header_step.addValues(values); |
| 609 | return config_header_step; | 642 | return config_header_step; |
| 610 | } | 643 | } |
| ... | @@ -669,8 +702,6 @@ pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCS | ... | @@ -669,8 +702,6 @@ pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCS |
| 669 | } | 702 | } |
| 670 | 703 | ||
| 671 | pub fn make(self: *Build, step_names: []const []const u8) !void { | 704 | pub fn make(self: *Build, step_names: []const []const u8) !void { |
| 672 | try self.makePath(self.cache_root); | ||
| 673 | |||
| 674 | var wanted_steps = ArrayList(*Step).init(self.allocator); | 705 | var wanted_steps = ArrayList(*Step).init(self.allocator); |
| 675 | defer wanted_steps.deinit(); | 706 | defer wanted_steps.deinit(); |
| 676 | 707 | ||
| ... | @@ -901,7 +932,7 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio | ... | @@ -901,7 +932,7 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio |
| 901 | return self.option( | 932 | return self.option( |
| 902 | std.builtin.Mode, | 933 | std.builtin.Mode, |
| 903 | "optimize", | 934 | "optimize", |
| 904 | "prioritize performance, safety, or binary size (-O flag)", | 935 | "Prioritize performance, safety, or binary size (-O flag)", |
| 905 | ) orelse .Debug; | 936 | ) orelse .Debug; |
| 906 | } | 937 | } |
| 907 | } | 938 | } |
| ... | @@ -1196,13 +1227,6 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, | ... | @@ -1196,13 +1227,6 @@ pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, |
| 1196 | } | 1227 | } |
| 1197 | } | 1228 | } |
| 1198 | 1229 | ||
| 1199 | pub fn makePath(self: *Build, path: []const u8) !void { | ||
| 1200 | fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { | ||
| 1201 | log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) }); | ||
| 1202 | return err; | ||
| 1203 | }; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | pub fn installArtifact(self: *Build, artifact: *CompileStep) void { | 1230 | pub fn installArtifact(self: *Build, artifact: *CompileStep) void { |
| 1207 | self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); | 1231 | self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); |
| 1208 | } | 1232 | } |
| ... | @@ -1317,8 +1341,8 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void { | ... | @@ -1317,8 +1341,8 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void { |
| 1317 | src_file.close(); | 1341 | src_file.close(); |
| 1318 | } | 1342 | } |
| 1319 | 1343 | ||
| 1320 | pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 { | 1344 | pub fn pathFromRoot(b: *Build, p: []const u8) []u8 { |
| 1321 | return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM"); | 1345 | return fs.path.resolve(b.allocator, &.{ b.build_root.path orelse ".", p }) catch @panic("OOM"); |
| 1322 | } | 1346 | } |
| 1323 | 1347 | ||
| 1324 | pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 { | 1348 | pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 { |
| ... | @@ -1539,10 +1563,19 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { | ... | @@ -1539,10 +1563,19 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { |
| 1539 | fn dependencyInner( | 1563 | fn dependencyInner( |
| 1540 | b: *Build, | 1564 | b: *Build, |
| 1541 | name: []const u8, | 1565 | name: []const u8, |
| 1542 | build_root: []const u8, | 1566 | build_root_string: []const u8, |
| 1543 | comptime build_zig: type, | 1567 | comptime build_zig: type, |
| 1544 | args: anytype, | 1568 | args: anytype, |
| 1545 | ) *Dependency { | 1569 | ) *Dependency { |
| 1570 | const build_root: std.Build.Cache.Directory = .{ | ||
| 1571 | .path = build_root_string, | ||
| 1572 | .handle = std.fs.cwd().openDir(build_root_string, .{}) catch |err| { | ||
| 1573 | std.debug.print("unable to open '{s}': {s}\n", .{ | ||
| 1574 | build_root_string, @errorName(err), | ||
| 1575 | }); | ||
| 1576 | std.process.exit(1); | ||
| 1577 | }, | ||
| 1578 | }; | ||
| 1546 | const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); | 1579 | const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); |
| 1547 | sub_builder.runBuild(build_zig) catch @panic("unhandled error"); | 1580 | sub_builder.runBuild(build_zig) catch @panic("unhandled error"); |
| 1548 | 1581 | ||
| ... | @@ -1563,26 +1596,6 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { | ... | @@ -1563,26 +1596,6 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { |
| 1563 | } | 1596 | } |
| 1564 | } | 1597 | } |
| 1565 | 1598 | ||
| 1566 | test "builder.findProgram compiles" { | ||
| 1567 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 1568 | |||
| 1569 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | ||
| 1570 | defer arena.deinit(); | ||
| 1571 | |||
| 1572 | const host = try NativeTargetInfo.detect(.{}); | ||
| 1573 | |||
| 1574 | const builder = try Build.create( | ||
| 1575 | arena.allocator(), | ||
| 1576 | "zig", | ||
| 1577 | "zig-cache", | ||
| 1578 | "zig-cache", | ||
| 1579 | "zig-cache", | ||
| 1580 | host, | ||
| 1581 | ); | ||
| 1582 | defer builder.destroy(); | ||
| 1583 | _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; | ||
| 1584 | } | ||
| 1585 | |||
| 1586 | pub const Module = struct { | 1599 | pub const Module = struct { |
| 1587 | builder: *Build, | 1600 | builder: *Build, |
| 1588 | /// This could either be a generated file, in which case the module | 1601 | /// This could either be a generated file, in which case the module |
| ... | @@ -1611,7 +1624,6 @@ pub const GeneratedFile = struct { | ... | @@ -1611,7 +1624,6 @@ pub const GeneratedFile = struct { |
| 1611 | }; | 1624 | }; |
| 1612 | 1625 | ||
| 1613 | /// A file source is a reference to an existing or future file. | 1626 | /// A file source is a reference to an existing or future file. |
| 1614 | /// | ||
| 1615 | pub const FileSource = union(enum) { | 1627 | pub const FileSource = union(enum) { |
| 1616 | /// A plain file path, relative to build root or absolute. | 1628 | /// A plain file path, relative to build root or absolute. |
| 1617 | path: []const u8, | 1629 | path: []const u8, |
lib/std/Build/Cache.zig created+1253| ... | @@ -0,0 +1,1253 @@ | ||
| 1 | //! Manages `zig-cache` directories. | ||
| 2 | //! This is not a general-purpose cache. It is designed to be fast and simple, | ||
| 3 | //! not to withstand attacks using specially-crafted input. | ||
| 4 | |||
| 5 | pub const Directory = struct { | ||
| 6 | /// This field is redundant for operations that can act on the open directory handle | ||
| 7 | /// directly, but it is needed when passing the directory to a child process. | ||
| 8 | /// `null` means cwd. | ||
| 9 | path: ?[]const u8, | ||
| 10 | handle: std.fs.Dir, | ||
| 11 | |||
| 12 | pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 { | ||
| 13 | if (self.path) |p| { | ||
| 14 | // TODO clean way to do this with only 1 allocation | ||
| 15 | const part2 = try std.fs.path.join(allocator, paths); | ||
| 16 | defer allocator.free(part2); | ||
| 17 | return std.fs.path.join(allocator, &[_][]const u8{ p, part2 }); | ||
| 18 | } else { | ||
| 19 | return std.fs.path.join(allocator, paths); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 { | ||
| 24 | if (self.path) |p| { | ||
| 25 | // TODO clean way to do this with only 1 allocation | ||
| 26 | const part2 = try std.fs.path.join(allocator, paths); | ||
| 27 | defer allocator.free(part2); | ||
| 28 | return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); | ||
| 29 | } else { | ||
| 30 | return std.fs.path.joinZ(allocator, paths); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Whether or not the handle should be closed, or the path should be freed | ||
| 35 | /// is determined by usage, however this function is provided for convenience | ||
| 36 | /// if it happens to be what the caller needs. | ||
| 37 | pub fn closeAndFree(self: *Directory, gpa: Allocator) void { | ||
| 38 | self.handle.close(); | ||
| 39 | if (self.path) |p| gpa.free(p); | ||
| 40 | self.* = undefined; | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | |||
| 44 | gpa: Allocator, | ||
| 45 | manifest_dir: fs.Dir, | ||
| 46 | hash: HashHelper = .{}, | ||
| 47 | /// This value is accessed from multiple threads, protected by mutex. | ||
| 48 | recent_problematic_timestamp: i128 = 0, | ||
| 49 | mutex: std.Thread.Mutex = .{}, | ||
| 50 | |||
| 51 | /// A set of strings such as the zig library directory or project source root, which | ||
| 52 | /// are stripped from the file paths before putting into the cache. They | ||
| 53 | /// are replaced with single-character indicators. This is not to save | ||
| 54 | /// space but to eliminate absolute file paths. This improves portability | ||
| 55 | /// and usefulness of the cache for advanced use cases. | ||
| 56 | prefixes_buffer: [4]Directory = undefined, | ||
| 57 | prefixes_len: usize = 0, | ||
| 58 | |||
| 59 | pub const DepTokenizer = @import("Cache/DepTokenizer.zig"); | ||
| 60 | |||
| 61 | const Cache = @This(); | ||
| 62 | const std = @import("std"); | ||
| 63 | const builtin = @import("builtin"); | ||
| 64 | const crypto = std.crypto; | ||
| 65 | const fs = std.fs; | ||
| 66 | const assert = std.debug.assert; | ||
| 67 | const testing = std.testing; | ||
| 68 | const mem = std.mem; | ||
| 69 | const fmt = std.fmt; | ||
| 70 | const Allocator = std.mem.Allocator; | ||
| 71 | const log = std.log.scoped(.cache); | ||
| 72 | |||
| 73 | pub fn addPrefix(cache: *Cache, directory: Directory) void { | ||
| 74 | cache.prefixes_buffer[cache.prefixes_len] = directory; | ||
| 75 | cache.prefixes_len += 1; | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Be sure to call `Manifest.deinit` after successful initialization. | ||
| 79 | pub fn obtain(cache: *Cache) Manifest { | ||
| 80 | return Manifest{ | ||
| 81 | .cache = cache, | ||
| 82 | .hash = cache.hash, | ||
| 83 | .manifest_file = null, | ||
| 84 | .manifest_dirty = false, | ||
| 85 | .hex_digest = undefined, | ||
| 86 | }; | ||
| 87 | } | ||
| 88 | |||
| 89 | pub fn prefixes(cache: *const Cache) []const Directory { | ||
| 90 | return cache.prefixes_buffer[0..cache.prefixes_len]; | ||
| 91 | } | ||
| 92 | |||
| 93 | const PrefixedPath = struct { | ||
| 94 | prefix: u8, | ||
| 95 | sub_path: []u8, | ||
| 96 | }; | ||
| 97 | |||
| 98 | fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { | ||
| 99 | const gpa = cache.gpa; | ||
| 100 | const resolved_path = try fs.path.resolve(gpa, &[_][]const u8{file_path}); | ||
| 101 | errdefer gpa.free(resolved_path); | ||
| 102 | return findPrefixResolved(cache, resolved_path); | ||
| 103 | } | ||
| 104 | |||
| 105 | /// Takes ownership of `resolved_path` on success. | ||
| 106 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { | ||
| 107 | const gpa = cache.gpa; | ||
| 108 | const prefixes_slice = cache.prefixes(); | ||
| 109 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. | ||
| 110 | while (i < prefixes_slice.len) : (i += 1) { | ||
| 111 | const p = prefixes_slice[i].path.?; | ||
| 112 | if (mem.startsWith(u8, resolved_path, p)) { | ||
| 113 | // +1 to skip over the path separator here | ||
| 114 | const sub_path = try gpa.dupe(u8, resolved_path[p.len + 1 ..]); | ||
| 115 | gpa.free(resolved_path); | ||
| 116 | return PrefixedPath{ | ||
| 117 | .prefix = @intCast(u8, i), | ||
| 118 | .sub_path = sub_path, | ||
| 119 | }; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | return PrefixedPath{ | ||
| 124 | .prefix = 0, | ||
| 125 | .sub_path = resolved_path, | ||
| 126 | }; | ||
| 127 | } | ||
| 128 | |||
| 129 | /// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 | ||
| 130 | pub const bin_digest_len = 16; | ||
| 131 | pub const hex_digest_len = bin_digest_len * 2; | ||
| 132 | pub const BinDigest = [bin_digest_len]u8; | ||
| 133 | |||
| 134 | const manifest_file_size_max = 50 * 1024 * 1024; | ||
| 135 | |||
| 136 | /// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it | ||
| 137 | /// provides enough collision resistance for the Manifest use cases, while being one of our | ||
| 138 | /// fastest options right now. | ||
| 139 | pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); | ||
| 140 | |||
| 141 | /// Initial state, that can be copied. | ||
| 142 | pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length); | ||
| 143 | |||
| 144 | pub const File = struct { | ||
| 145 | prefixed_path: ?PrefixedPath, | ||
| 146 | max_file_size: ?usize, | ||
| 147 | stat: Stat, | ||
| 148 | bin_digest: BinDigest, | ||
| 149 | contents: ?[]const u8, | ||
| 150 | |||
| 151 | pub const Stat = struct { | ||
| 152 | inode: fs.File.INode, | ||
| 153 | size: u64, | ||
| 154 | mtime: i128, | ||
| 155 | }; | ||
| 156 | |||
| 157 | pub fn deinit(self: *File, gpa: Allocator) void { | ||
| 158 | if (self.prefixed_path) |pp| { | ||
| 159 | gpa.free(pp.sub_path); | ||
| 160 | self.prefixed_path = null; | ||
| 161 | } | ||
| 162 | if (self.contents) |contents| { | ||
| 163 | gpa.free(contents); | ||
| 164 | self.contents = null; | ||
| 165 | } | ||
| 166 | self.* = undefined; | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | |||
| 170 | pub const HashHelper = struct { | ||
| 171 | hasher: Hasher = hasher_init, | ||
| 172 | |||
| 173 | /// Record a slice of bytes as an dependency of the process being cached | ||
| 174 | pub fn addBytes(hh: *HashHelper, bytes: []const u8) void { | ||
| 175 | hh.hasher.update(mem.asBytes(&bytes.len)); | ||
| 176 | hh.hasher.update(bytes); | ||
| 177 | } | ||
| 178 | |||
| 179 | pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void { | ||
| 180 | hh.add(optional_bytes != null); | ||
| 181 | hh.addBytes(optional_bytes orelse return); | ||
| 182 | } | ||
| 183 | |||
| 184 | pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void { | ||
| 185 | hh.add(list_of_bytes.len); | ||
| 186 | for (list_of_bytes) |bytes| hh.addBytes(bytes); | ||
| 187 | } | ||
| 188 | |||
| 189 | /// Convert the input value into bytes and record it as a dependency of the process being cached. | ||
| 190 | pub fn add(hh: *HashHelper, x: anytype) void { | ||
| 191 | switch (@TypeOf(x)) { | ||
| 192 | std.builtin.Version => { | ||
| 193 | hh.add(x.major); | ||
| 194 | hh.add(x.minor); | ||
| 195 | hh.add(x.patch); | ||
| 196 | }, | ||
| 197 | std.Target.Os.TaggedVersionRange => { | ||
| 198 | switch (x) { | ||
| 199 | .linux => |linux| { | ||
| 200 | hh.add(linux.range.min); | ||
| 201 | hh.add(linux.range.max); | ||
| 202 | hh.add(linux.glibc); | ||
| 203 | }, | ||
| 204 | .windows => |windows| { | ||
| 205 | hh.add(windows.min); | ||
| 206 | hh.add(windows.max); | ||
| 207 | }, | ||
| 208 | .semver => |semver| { | ||
| 209 | hh.add(semver.min); | ||
| 210 | hh.add(semver.max); | ||
| 211 | }, | ||
| 212 | .none => {}, | ||
| 213 | } | ||
| 214 | }, | ||
| 215 | else => switch (@typeInfo(@TypeOf(x))) { | ||
| 216 | .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)), | ||
| 217 | else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))), | ||
| 218 | }, | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | pub fn addOptional(hh: *HashHelper, optional: anytype) void { | ||
| 223 | hh.add(optional != null); | ||
| 224 | hh.add(optional orelse return); | ||
| 225 | } | ||
| 226 | |||
| 227 | /// Returns a hex encoded hash of the inputs, without modifying state. | ||
| 228 | pub fn peek(hh: HashHelper) [hex_digest_len]u8 { | ||
| 229 | var copy = hh; | ||
| 230 | return copy.final(); | ||
| 231 | } | ||
| 232 | |||
| 233 | pub fn peekBin(hh: HashHelper) BinDigest { | ||
| 234 | var copy = hh; | ||
| 235 | var bin_digest: BinDigest = undefined; | ||
| 236 | copy.hasher.final(&bin_digest); | ||
| 237 | return bin_digest; | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Returns a hex encoded hash of the inputs, mutating the state of the hasher. | ||
| 241 | pub fn final(hh: *HashHelper) [hex_digest_len]u8 { | ||
| 242 | var bin_digest: BinDigest = undefined; | ||
| 243 | hh.hasher.final(&bin_digest); | ||
| 244 | |||
| 245 | var out_digest: [hex_digest_len]u8 = undefined; | ||
| 246 | _ = std.fmt.bufPrint( | ||
| 247 | &out_digest, | ||
| 248 | "{s}", | ||
| 249 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | ||
| 250 | ) catch unreachable; | ||
| 251 | return out_digest; | ||
| 252 | } | ||
| 253 | }; | ||
| 254 | |||
| 255 | pub const Lock = struct { | ||
| 256 | manifest_file: fs.File, | ||
| 257 | |||
| 258 | pub fn release(lock: *Lock) void { | ||
| 259 | if (builtin.os.tag == .windows) { | ||
| 260 | // Windows does not guarantee that locks are immediately unlocked when | ||
| 261 | // the file handle is closed. See LockFileEx documentation. | ||
| 262 | lock.manifest_file.unlock(); | ||
| 263 | } | ||
| 264 | |||
| 265 | lock.manifest_file.close(); | ||
| 266 | lock.* = undefined; | ||
| 267 | } | ||
| 268 | }; | ||
| 269 | |||
| 270 | pub const Manifest = struct { | ||
| 271 | cache: *Cache, | ||
| 272 | /// Current state for incremental hashing. | ||
| 273 | hash: HashHelper, | ||
| 274 | manifest_file: ?fs.File, | ||
| 275 | manifest_dirty: bool, | ||
| 276 | /// Set this flag to true before calling hit() in order to indicate that | ||
| 277 | /// upon a cache hit, the code using the cache will not modify the files | ||
| 278 | /// within the cache directory. This allows multiple processes to utilize | ||
| 279 | /// the same cache directory at the same time. | ||
| 280 | want_shared_lock: bool = true, | ||
| 281 | have_exclusive_lock: bool = false, | ||
| 282 | // Indicate that we want isProblematicTimestamp to perform a filesystem write in | ||
| 283 | // order to obtain a problematic timestamp for the next call. Calls after that | ||
| 284 | // will then use the same timestamp, to avoid unnecessary filesystem writes. | ||
| 285 | want_refresh_timestamp: bool = true, | ||
| 286 | files: std.ArrayListUnmanaged(File) = .{}, | ||
| 287 | hex_digest: [hex_digest_len]u8, | ||
| 288 | /// Populated when hit() returns an error because of one | ||
| 289 | /// of the files listed in the manifest. | ||
| 290 | failed_file_index: ?usize = null, | ||
| 291 | /// Keeps track of the last time we performed a file system write to observe | ||
| 292 | /// what time the file system thinks it is, according to its own granularity. | ||
| 293 | recent_problematic_timestamp: i128 = 0, | ||
| 294 | |||
| 295 | /// Add a file as a dependency of process being cached. When `hit` is | ||
| 296 | /// called, the file's contents will be checked to ensure that it matches | ||
| 297 | /// the contents from previous times. | ||
| 298 | /// | ||
| 299 | /// Max file size will be used to determine the amount of space the file contents | ||
| 300 | /// are allowed to take up in memory. If max_file_size is null, then the contents | ||
| 301 | /// will not be loaded into memory. | ||
| 302 | /// | ||
| 303 | /// Returns the index of the entry in the `files` array list. You can use it | ||
| 304 | /// to access the contents of the file after calling `hit()` like so: | ||
| 305 | /// | ||
| 306 | /// ``` | ||
| 307 | /// var file_contents = cache_hash.files.items[file_index].contents.?; | ||
| 308 | /// ``` | ||
| 309 | pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize { | ||
| 310 | assert(self.manifest_file == null); | ||
| 311 | |||
| 312 | const gpa = self.cache.gpa; | ||
| 313 | try self.files.ensureUnusedCapacity(gpa, 1); | ||
| 314 | const prefixed_path = try self.cache.findPrefix(file_path); | ||
| 315 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 316 | |||
| 317 | self.files.addOneAssumeCapacity().* = .{ | ||
| 318 | .prefixed_path = prefixed_path, | ||
| 319 | .contents = null, | ||
| 320 | .max_file_size = max_file_size, | ||
| 321 | .stat = undefined, | ||
| 322 | .bin_digest = undefined, | ||
| 323 | }; | ||
| 324 | |||
| 325 | self.hash.add(prefixed_path.prefix); | ||
| 326 | self.hash.addBytes(prefixed_path.sub_path); | ||
| 327 | |||
| 328 | return self.files.items.len - 1; | ||
| 329 | } | ||
| 330 | |||
| 331 | pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void { | ||
| 332 | self.hash.add(optional_file_path != null); | ||
| 333 | const file_path = optional_file_path orelse return; | ||
| 334 | _ = try self.addFile(file_path, null); | ||
| 335 | } | ||
| 336 | |||
| 337 | pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void { | ||
| 338 | self.hash.add(list_of_files.len); | ||
| 339 | for (list_of_files) |file_path| { | ||
| 340 | _ = try self.addFile(file_path, null); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | /// Check the cache to see if the input exists in it. If it exists, returns `true`. | ||
| 345 | /// A hex encoding of its hash is available by calling `final`. | ||
| 346 | /// | ||
| 347 | /// This function will also acquire an exclusive lock to the manifest file. This means | ||
| 348 | /// that a process holding a Manifest will block any other process attempting to | ||
| 349 | /// acquire the lock. If `want_shared_lock` is `true`, a cache hit guarantees the | ||
| 350 | /// manifest file to be locked in shared mode, and a cache miss guarantees the manifest | ||
| 351 | /// file to be locked in exclusive mode. | ||
| 352 | /// | ||
| 353 | /// The lock on the manifest file is released when `deinit` is called. As another | ||
| 354 | /// option, one may call `toOwnedLock` to obtain a smaller object which can represent | ||
| 355 | /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called. | ||
| 356 | pub fn hit(self: *Manifest) !bool { | ||
| 357 | const gpa = self.cache.gpa; | ||
| 358 | assert(self.manifest_file == null); | ||
| 359 | |||
| 360 | self.failed_file_index = null; | ||
| 361 | |||
| 362 | const ext = ".txt"; | ||
| 363 | var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined; | ||
| 364 | |||
| 365 | var bin_digest: BinDigest = undefined; | ||
| 366 | self.hash.hasher.final(&bin_digest); | ||
| 367 | |||
| 368 | _ = std.fmt.bufPrint( | ||
| 369 | &self.hex_digest, | ||
| 370 | "{s}", | ||
| 371 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | ||
| 372 | ) catch unreachable; | ||
| 373 | |||
| 374 | self.hash.hasher = hasher_init; | ||
| 375 | self.hash.hasher.update(&bin_digest); | ||
| 376 | |||
| 377 | mem.copy(u8, &manifest_file_path, &self.hex_digest); | ||
| 378 | manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*; | ||
| 379 | |||
| 380 | if (self.files.items.len == 0) { | ||
| 381 | // If there are no file inputs, we check if the manifest file exists instead of | ||
| 382 | // comparing the hashes on the files used for the cached item | ||
| 383 | while (true) { | ||
| 384 | if (self.cache.manifest_dir.openFile(&manifest_file_path, .{ | ||
| 385 | .mode = .read_write, | ||
| 386 | .lock = .Exclusive, | ||
| 387 | .lock_nonblocking = self.want_shared_lock, | ||
| 388 | })) |manifest_file| { | ||
| 389 | self.manifest_file = manifest_file; | ||
| 390 | self.have_exclusive_lock = true; | ||
| 391 | break; | ||
| 392 | } else |open_err| switch (open_err) { | ||
| 393 | error.WouldBlock => { | ||
| 394 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | ||
| 395 | .lock = .Shared, | ||
| 396 | }); | ||
| 397 | break; | ||
| 398 | }, | ||
| 399 | error.FileNotFound => { | ||
| 400 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ | ||
| 401 | .read = true, | ||
| 402 | .truncate = false, | ||
| 403 | .lock = .Exclusive, | ||
| 404 | .lock_nonblocking = self.want_shared_lock, | ||
| 405 | })) |manifest_file| { | ||
| 406 | self.manifest_file = manifest_file; | ||
| 407 | self.manifest_dirty = true; | ||
| 408 | self.have_exclusive_lock = true; | ||
| 409 | return false; // cache miss; exclusive lock already held | ||
| 410 | } else |err| switch (err) { | ||
| 411 | error.WouldBlock => continue, | ||
| 412 | else => |e| return e, | ||
| 413 | } | ||
| 414 | }, | ||
| 415 | else => |e| return e, | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } else { | ||
| 419 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ | ||
| 420 | .read = true, | ||
| 421 | .truncate = false, | ||
| 422 | .lock = .Exclusive, | ||
| 423 | .lock_nonblocking = self.want_shared_lock, | ||
| 424 | })) |manifest_file| { | ||
| 425 | self.manifest_file = manifest_file; | ||
| 426 | self.have_exclusive_lock = true; | ||
| 427 | } else |err| switch (err) { | ||
| 428 | error.WouldBlock => { | ||
| 429 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | ||
| 430 | .lock = .Shared, | ||
| 431 | }); | ||
| 432 | }, | ||
| 433 | else => |e| return e, | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | self.want_refresh_timestamp = true; | ||
| 438 | |||
| 439 | const file_contents = try self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max); | ||
| 440 | defer gpa.free(file_contents); | ||
| 441 | |||
| 442 | const input_file_count = self.files.items.len; | ||
| 443 | var any_file_changed = false; | ||
| 444 | var line_iter = mem.tokenize(u8, file_contents, "\n"); | ||
| 445 | var idx: usize = 0; | ||
| 446 | while (line_iter.next()) |line| { | ||
| 447 | defer idx += 1; | ||
| 448 | |||
| 449 | const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: { | ||
| 450 | const new = try self.files.addOne(gpa); | ||
| 451 | new.* = .{ | ||
| 452 | .prefixed_path = null, | ||
| 453 | .contents = null, | ||
| 454 | .max_file_size = null, | ||
| 455 | .stat = undefined, | ||
| 456 | .bin_digest = undefined, | ||
| 457 | }; | ||
| 458 | break :blk new; | ||
| 459 | }; | ||
| 460 | |||
| 461 | var iter = mem.tokenize(u8, line, " "); | ||
| 462 | const size = iter.next() orelse return error.InvalidFormat; | ||
| 463 | const inode = iter.next() orelse return error.InvalidFormat; | ||
| 464 | const mtime_nsec_str = iter.next() orelse return error.InvalidFormat; | ||
| 465 | const digest_str = iter.next() orelse return error.InvalidFormat; | ||
| 466 | const prefix_str = iter.next() orelse return error.InvalidFormat; | ||
| 467 | const file_path = iter.rest(); | ||
| 468 | |||
| 469 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; | ||
| 470 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; | ||
| 471 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; | ||
| 472 | _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; | ||
| 473 | const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; | ||
| 474 | if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; | ||
| 475 | |||
| 476 | if (file_path.len == 0) { | ||
| 477 | return error.InvalidFormat; | ||
| 478 | } | ||
| 479 | if (cache_hash_file.prefixed_path) |pp| { | ||
| 480 | if (pp.prefix != prefix or !mem.eql(u8, file_path, pp.sub_path)) { | ||
| 481 | return error.InvalidFormat; | ||
| 482 | } | ||
| 483 | } | ||
| 484 | |||
| 485 | if (cache_hash_file.prefixed_path == null) { | ||
| 486 | cache_hash_file.prefixed_path = .{ | ||
| 487 | .prefix = prefix, | ||
| 488 | .sub_path = try gpa.dupe(u8, file_path), | ||
| 489 | }; | ||
| 490 | } | ||
| 491 | |||
| 492 | const pp = cache_hash_file.prefixed_path.?; | ||
| 493 | const dir = self.cache.prefixes()[pp.prefix].handle; | ||
| 494 | const this_file = dir.openFile(pp.sub_path, .{ .mode = .read_only }) catch |err| switch (err) { | ||
| 495 | error.FileNotFound => { | ||
| 496 | try self.upgradeToExclusiveLock(); | ||
| 497 | return false; | ||
| 498 | }, | ||
| 499 | else => return error.CacheUnavailable, | ||
| 500 | }; | ||
| 501 | defer this_file.close(); | ||
| 502 | |||
| 503 | const actual_stat = this_file.stat() catch |err| { | ||
| 504 | self.failed_file_index = idx; | ||
| 505 | return err; | ||
| 506 | }; | ||
| 507 | const size_match = actual_stat.size == cache_hash_file.stat.size; | ||
| 508 | const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime; | ||
| 509 | const inode_match = actual_stat.inode == cache_hash_file.stat.inode; | ||
| 510 | |||
| 511 | if (!size_match or !mtime_match or !inode_match) { | ||
| 512 | self.manifest_dirty = true; | ||
| 513 | |||
| 514 | cache_hash_file.stat = .{ | ||
| 515 | .size = actual_stat.size, | ||
| 516 | .mtime = actual_stat.mtime, | ||
| 517 | .inode = actual_stat.inode, | ||
| 518 | }; | ||
| 519 | |||
| 520 | if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) { | ||
| 521 | // The actual file has an unreliable timestamp, force it to be hashed | ||
| 522 | cache_hash_file.stat.mtime = 0; | ||
| 523 | cache_hash_file.stat.inode = 0; | ||
| 524 | } | ||
| 525 | |||
| 526 | var actual_digest: BinDigest = undefined; | ||
| 527 | hashFile(this_file, &actual_digest) catch |err| { | ||
| 528 | self.failed_file_index = idx; | ||
| 529 | return err; | ||
| 530 | }; | ||
| 531 | |||
| 532 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { | ||
| 533 | cache_hash_file.bin_digest = actual_digest; | ||
| 534 | // keep going until we have the input file digests | ||
| 535 | any_file_changed = true; | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | if (!any_file_changed) { | ||
| 540 | self.hash.hasher.update(&cache_hash_file.bin_digest); | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | if (any_file_changed) { | ||
| 545 | // cache miss | ||
| 546 | // keep the manifest file open | ||
| 547 | self.unhit(bin_digest, input_file_count); | ||
| 548 | try self.upgradeToExclusiveLock(); | ||
| 549 | return false; | ||
| 550 | } | ||
| 551 | |||
| 552 | if (idx < input_file_count) { | ||
| 553 | self.manifest_dirty = true; | ||
| 554 | while (idx < input_file_count) : (idx += 1) { | ||
| 555 | const ch_file = &self.files.items[idx]; | ||
| 556 | self.populateFileHash(ch_file) catch |err| { | ||
| 557 | self.failed_file_index = idx; | ||
| 558 | return err; | ||
| 559 | }; | ||
| 560 | } | ||
| 561 | try self.upgradeToExclusiveLock(); | ||
| 562 | return false; | ||
| 563 | } | ||
| 564 | |||
| 565 | if (self.want_shared_lock) { | ||
| 566 | try self.downgradeToSharedLock(); | ||
| 567 | } | ||
| 568 | |||
| 569 | return true; | ||
| 570 | } | ||
| 571 | |||
| 572 | pub fn unhit(self: *Manifest, bin_digest: BinDigest, input_file_count: usize) void { | ||
| 573 | // Reset the hash. | ||
| 574 | self.hash.hasher = hasher_init; | ||
| 575 | self.hash.hasher.update(&bin_digest); | ||
| 576 | |||
| 577 | // Remove files not in the initial hash. | ||
| 578 | for (self.files.items[input_file_count..]) |*file| { | ||
| 579 | file.deinit(self.cache.gpa); | ||
| 580 | } | ||
| 581 | self.files.shrinkRetainingCapacity(input_file_count); | ||
| 582 | |||
| 583 | for (self.files.items) |file| { | ||
| 584 | self.hash.hasher.update(&file.bin_digest); | ||
| 585 | } | ||
| 586 | } | ||
| 587 | |||
| 588 | fn isProblematicTimestamp(man: *Manifest, file_time: i128) bool { | ||
| 589 | // If the file_time is prior to the most recent problematic timestamp | ||
| 590 | // then we don't need to access the filesystem. | ||
| 591 | if (file_time < man.recent_problematic_timestamp) | ||
| 592 | return false; | ||
| 593 | |||
| 594 | // Next we will check the globally shared Cache timestamp, which is accessed | ||
| 595 | // from multiple threads. | ||
| 596 | man.cache.mutex.lock(); | ||
| 597 | defer man.cache.mutex.unlock(); | ||
| 598 | |||
| 599 | // Save the global one to our local one to avoid locking next time. | ||
| 600 | man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp; | ||
| 601 | if (file_time < man.recent_problematic_timestamp) | ||
| 602 | return false; | ||
| 603 | |||
| 604 | // This flag prevents multiple filesystem writes for the same hit() call. | ||
| 605 | if (man.want_refresh_timestamp) { | ||
| 606 | man.want_refresh_timestamp = false; | ||
| 607 | |||
| 608 | var file = man.cache.manifest_dir.createFile("timestamp", .{ | ||
| 609 | .read = true, | ||
| 610 | .truncate = true, | ||
| 611 | }) catch return true; | ||
| 612 | defer file.close(); | ||
| 613 | |||
| 614 | // Save locally and also save globally (we still hold the global lock). | ||
| 615 | man.recent_problematic_timestamp = (file.stat() catch return true).mtime; | ||
| 616 | man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp; | ||
| 617 | } | ||
| 618 | |||
| 619 | return file_time >= man.recent_problematic_timestamp; | ||
| 620 | } | ||
| 621 | |||
| 622 | fn populateFileHash(self: *Manifest, ch_file: *File) !void { | ||
| 623 | const pp = ch_file.prefixed_path.?; | ||
| 624 | const dir = self.cache.prefixes()[pp.prefix].handle; | ||
| 625 | const file = try dir.openFile(pp.sub_path, .{}); | ||
| 626 | defer file.close(); | ||
| 627 | |||
| 628 | const actual_stat = try file.stat(); | ||
| 629 | ch_file.stat = .{ | ||
| 630 | .size = actual_stat.size, | ||
| 631 | .mtime = actual_stat.mtime, | ||
| 632 | .inode = actual_stat.inode, | ||
| 633 | }; | ||
| 634 | |||
| 635 | if (self.isProblematicTimestamp(ch_file.stat.mtime)) { | ||
| 636 | // The actual file has an unreliable timestamp, force it to be hashed | ||
| 637 | ch_file.stat.mtime = 0; | ||
| 638 | ch_file.stat.inode = 0; | ||
| 639 | } | ||
| 640 | |||
| 641 | if (ch_file.max_file_size) |max_file_size| { | ||
| 642 | if (ch_file.stat.size > max_file_size) { | ||
| 643 | return error.FileTooBig; | ||
| 644 | } | ||
| 645 | |||
| 646 | const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size)); | ||
| 647 | errdefer self.cache.gpa.free(contents); | ||
| 648 | |||
| 649 | // Hash while reading from disk, to keep the contents in the cpu cache while | ||
| 650 | // doing hashing. | ||
| 651 | var hasher = hasher_init; | ||
| 652 | var off: usize = 0; | ||
| 653 | while (true) { | ||
| 654 | // give me everything you've got, captain | ||
| 655 | const bytes_read = try file.read(contents[off..]); | ||
| 656 | if (bytes_read == 0) break; | ||
| 657 | hasher.update(contents[off..][0..bytes_read]); | ||
| 658 | off += bytes_read; | ||
| 659 | } | ||
| 660 | hasher.final(&ch_file.bin_digest); | ||
| 661 | |||
| 662 | ch_file.contents = contents; | ||
| 663 | } else { | ||
| 664 | try hashFile(file, &ch_file.bin_digest); | ||
| 665 | } | ||
| 666 | |||
| 667 | self.hash.hasher.update(&ch_file.bin_digest); | ||
| 668 | } | ||
| 669 | |||
| 670 | /// Add a file as a dependency of process being cached, after the initial hash has been | ||
| 671 | /// calculated. This is useful for processes that don't know all the files that | ||
| 672 | /// are depended on ahead of time. For example, a source file that can import other files | ||
| 673 | /// will need to be recompiled if the imported file is changed. | ||
| 674 | pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 { | ||
| 675 | assert(self.manifest_file != null); | ||
| 676 | |||
| 677 | const gpa = self.cache.gpa; | ||
| 678 | const prefixed_path = try self.cache.findPrefix(file_path); | ||
| 679 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 680 | |||
| 681 | const new_ch_file = try self.files.addOne(gpa); | ||
| 682 | new_ch_file.* = .{ | ||
| 683 | .prefixed_path = prefixed_path, | ||
| 684 | .max_file_size = max_file_size, | ||
| 685 | .stat = undefined, | ||
| 686 | .bin_digest = undefined, | ||
| 687 | .contents = null, | ||
| 688 | }; | ||
| 689 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); | ||
| 690 | |||
| 691 | try self.populateFileHash(new_ch_file); | ||
| 692 | |||
| 693 | return new_ch_file.contents.?; | ||
| 694 | } | ||
| 695 | |||
| 696 | /// Add a file as a dependency of process being cached, after the initial hash has been | ||
| 697 | /// calculated. This is useful for processes that don't know the all the files that | ||
| 698 | /// are depended on ahead of time. For example, a source file that can import other files | ||
| 699 | /// will need to be recompiled if the imported file is changed. | ||
| 700 | pub fn addFilePost(self: *Manifest, file_path: []const u8) !void { | ||
| 701 | assert(self.manifest_file != null); | ||
| 702 | |||
| 703 | const gpa = self.cache.gpa; | ||
| 704 | const prefixed_path = try self.cache.findPrefix(file_path); | ||
| 705 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 706 | |||
| 707 | const new_ch_file = try self.files.addOne(gpa); | ||
| 708 | new_ch_file.* = .{ | ||
| 709 | .prefixed_path = prefixed_path, | ||
| 710 | .max_file_size = null, | ||
| 711 | .stat = undefined, | ||
| 712 | .bin_digest = undefined, | ||
| 713 | .contents = null, | ||
| 714 | }; | ||
| 715 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); | ||
| 716 | |||
| 717 | try self.populateFileHash(new_ch_file); | ||
| 718 | } | ||
| 719 | |||
| 720 | /// Like `addFilePost` but when the file contents have already been loaded from disk. | ||
| 721 | /// On success, cache takes ownership of `resolved_path`. | ||
| 722 | pub fn addFilePostContents( | ||
| 723 | self: *Manifest, | ||
| 724 | resolved_path: []u8, | ||
| 725 | bytes: []const u8, | ||
| 726 | stat: File.Stat, | ||
| 727 | ) error{OutOfMemory}!void { | ||
| 728 | assert(self.manifest_file != null); | ||
| 729 | const gpa = self.cache.gpa; | ||
| 730 | |||
| 731 | const ch_file = try self.files.addOne(gpa); | ||
| 732 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); | ||
| 733 | |||
| 734 | const prefixed_path = try self.cache.findPrefixResolved(resolved_path); | ||
| 735 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 736 | |||
| 737 | ch_file.* = .{ | ||
| 738 | .prefixed_path = prefixed_path, | ||
| 739 | .max_file_size = null, | ||
| 740 | .stat = stat, | ||
| 741 | .bin_digest = undefined, | ||
| 742 | .contents = null, | ||
| 743 | }; | ||
| 744 | |||
| 745 | if (self.isProblematicTimestamp(ch_file.stat.mtime)) { | ||
| 746 | // The actual file has an unreliable timestamp, force it to be hashed | ||
| 747 | ch_file.stat.mtime = 0; | ||
| 748 | ch_file.stat.inode = 0; | ||
| 749 | } | ||
| 750 | |||
| 751 | { | ||
| 752 | var hasher = hasher_init; | ||
| 753 | hasher.update(bytes); | ||
| 754 | hasher.final(&ch_file.bin_digest); | ||
| 755 | } | ||
| 756 | |||
| 757 | self.hash.hasher.update(&ch_file.bin_digest); | ||
| 758 | } | ||
| 759 | |||
| 760 | pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void { | ||
| 761 | assert(self.manifest_file != null); | ||
| 762 | |||
| 763 | const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max); | ||
| 764 | defer self.cache.gpa.free(dep_file_contents); | ||
| 765 | |||
| 766 | var error_buf = std.ArrayList(u8).init(self.cache.gpa); | ||
| 767 | defer error_buf.deinit(); | ||
| 768 | |||
| 769 | var it: DepTokenizer = .{ .bytes = dep_file_contents }; | ||
| 770 | |||
| 771 | // Skip first token: target. | ||
| 772 | switch (it.next() orelse return) { // Empty dep file OK. | ||
| 773 | .target, .target_must_resolve, .prereq => {}, | ||
| 774 | else => |err| { | ||
| 775 | try err.printError(error_buf.writer()); | ||
| 776 | log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); | ||
| 777 | return error.InvalidDepFile; | ||
| 778 | }, | ||
| 779 | } | ||
| 780 | // Process 0+ preqreqs. | ||
| 781 | // Clang is invoked in single-source mode so we never get more targets. | ||
| 782 | while (true) { | ||
| 783 | switch (it.next() orelse return) { | ||
| 784 | .target, .target_must_resolve => return, | ||
| 785 | .prereq => |file_path| try self.addFilePost(file_path), | ||
| 786 | else => |err| { | ||
| 787 | try err.printError(error_buf.writer()); | ||
| 788 | log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); | ||
| 789 | return error.InvalidDepFile; | ||
| 790 | }, | ||
| 791 | } | ||
| 792 | } | ||
| 793 | } | ||
| 794 | |||
| 795 | /// Returns a hex encoded hash of the inputs. | ||
| 796 | pub fn final(self: *Manifest) [hex_digest_len]u8 { | ||
| 797 | assert(self.manifest_file != null); | ||
| 798 | |||
| 799 | // We don't close the manifest file yet, because we want to | ||
| 800 | // keep it locked until the API user is done using it. | ||
| 801 | // We also don't write out the manifest yet, because until | ||
| 802 | // cache_release is called we still might be working on creating | ||
| 803 | // the artifacts to cache. | ||
| 804 | |||
| 805 | var bin_digest: BinDigest = undefined; | ||
| 806 | self.hash.hasher.final(&bin_digest); | ||
| 807 | |||
| 808 | var out_digest: [hex_digest_len]u8 = undefined; | ||
| 809 | _ = std.fmt.bufPrint( | ||
| 810 | &out_digest, | ||
| 811 | "{s}", | ||
| 812 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | ||
| 813 | ) catch unreachable; | ||
| 814 | |||
| 815 | return out_digest; | ||
| 816 | } | ||
| 817 | |||
| 818 | /// If `want_shared_lock` is true, this function automatically downgrades the | ||
| 819 | /// lock from exclusive to shared. | ||
| 820 | pub fn writeManifest(self: *Manifest) !void { | ||
| 821 | assert(self.have_exclusive_lock); | ||
| 822 | |||
| 823 | const manifest_file = self.manifest_file.?; | ||
| 824 | if (self.manifest_dirty) { | ||
| 825 | self.manifest_dirty = false; | ||
| 826 | |||
| 827 | var contents = std.ArrayList(u8).init(self.cache.gpa); | ||
| 828 | defer contents.deinit(); | ||
| 829 | |||
| 830 | const writer = contents.writer(); | ||
| 831 | var encoded_digest: [hex_digest_len]u8 = undefined; | ||
| 832 | |||
| 833 | for (self.files.items) |file| { | ||
| 834 | _ = std.fmt.bufPrint( | ||
| 835 | &encoded_digest, | ||
| 836 | "{s}", | ||
| 837 | .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, | ||
| 838 | ) catch unreachable; | ||
| 839 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ | ||
| 840 | file.stat.size, | ||
| 841 | file.stat.inode, | ||
| 842 | file.stat.mtime, | ||
| 843 | &encoded_digest, | ||
| 844 | file.prefixed_path.?.prefix, | ||
| 845 | file.prefixed_path.?.sub_path, | ||
| 846 | }); | ||
| 847 | } | ||
| 848 | |||
| 849 | try manifest_file.setEndPos(contents.items.len); | ||
| 850 | try manifest_file.pwriteAll(contents.items, 0); | ||
| 851 | } | ||
| 852 | |||
| 853 | if (self.want_shared_lock) { | ||
| 854 | try self.downgradeToSharedLock(); | ||
| 855 | } | ||
| 856 | } | ||
| 857 | |||
| 858 | fn downgradeToSharedLock(self: *Manifest) !void { | ||
| 859 | if (!self.have_exclusive_lock) return; | ||
| 860 | |||
| 861 | // WASI does not currently support flock, so we bypass it here. | ||
| 862 | // TODO: If/when flock is supported on WASI, this check should be removed. | ||
| 863 | // See https://github.com/WebAssembly/wasi-filesystem/issues/2 | ||
| 864 | if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) { | ||
| 865 | const manifest_file = self.manifest_file.?; | ||
| 866 | try manifest_file.downgradeLock(); | ||
| 867 | } | ||
| 868 | |||
| 869 | self.have_exclusive_lock = false; | ||
| 870 | } | ||
| 871 | |||
| 872 | fn upgradeToExclusiveLock(self: *Manifest) !void { | ||
| 873 | if (self.have_exclusive_lock) return; | ||
| 874 | assert(self.manifest_file != null); | ||
| 875 | |||
| 876 | // WASI does not currently support flock, so we bypass it here. | ||
| 877 | // TODO: If/when flock is supported on WASI, this check should be removed. | ||
| 878 | // See https://github.com/WebAssembly/wasi-filesystem/issues/2 | ||
| 879 | if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) { | ||
| 880 | const manifest_file = self.manifest_file.?; | ||
| 881 | // Here we intentionally have a period where the lock is released, in case there are | ||
| 882 | // other processes holding a shared lock. | ||
| 883 | manifest_file.unlock(); | ||
| 884 | try manifest_file.lock(.Exclusive); | ||
| 885 | } | ||
| 886 | self.have_exclusive_lock = true; | ||
| 887 | } | ||
| 888 | |||
| 889 | /// Obtain only the data needed to maintain a lock on the manifest file. | ||
| 890 | /// The `Manifest` remains safe to deinit. | ||
| 891 | /// Don't forget to call `writeManifest` before this! | ||
| 892 | pub fn toOwnedLock(self: *Manifest) Lock { | ||
| 893 | const lock: Lock = .{ | ||
| 894 | .manifest_file = self.manifest_file.?, | ||
| 895 | }; | ||
| 896 | |||
| 897 | self.manifest_file = null; | ||
| 898 | return lock; | ||
| 899 | } | ||
| 900 | |||
| 901 | /// Releases the manifest file and frees any memory the Manifest was using. | ||
| 902 | /// `Manifest.hit` must be called first. | ||
| 903 | /// Don't forget to call `writeManifest` before this! | ||
| 904 | pub fn deinit(self: *Manifest) void { | ||
| 905 | if (self.manifest_file) |file| { | ||
| 906 | if (builtin.os.tag == .windows) { | ||
| 907 | // See Lock.release for why this is required on Windows | ||
| 908 | file.unlock(); | ||
| 909 | } | ||
| 910 | |||
| 911 | file.close(); | ||
| 912 | } | ||
| 913 | for (self.files.items) |*file| { | ||
| 914 | file.deinit(self.cache.gpa); | ||
| 915 | } | ||
| 916 | self.files.deinit(self.cache.gpa); | ||
| 917 | } | ||
| 918 | }; | ||
| 919 | |||
| 920 | /// On operating systems that support symlinks, does a readlink. On other operating systems, | ||
| 921 | /// uses the file contents. Windows supports symlinks but only with elevated privileges, so | ||
| 922 | /// it is treated as not supporting symlinks. | ||
| 923 | pub fn readSmallFile(dir: fs.Dir, sub_path: []const u8, buffer: []u8) ![]u8 { | ||
| 924 | if (builtin.os.tag == .windows) { | ||
| 925 | return dir.readFile(sub_path, buffer); | ||
| 926 | } else { | ||
| 927 | return dir.readLink(sub_path, buffer); | ||
| 928 | } | ||
| 929 | } | ||
| 930 | |||
| 931 | /// On operating systems that support symlinks, does a symlink. On other operating systems, | ||
| 932 | /// uses the file contents. Windows supports symlinks but only with elevated privileges, so | ||
| 933 | /// it is treated as not supporting symlinks. | ||
| 934 | /// `data` must be a valid UTF-8 encoded file path and 255 bytes or fewer. | ||
| 935 | pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void { | ||
| 936 | assert(data.len <= 255); | ||
| 937 | if (builtin.os.tag == .windows) { | ||
| 938 | return dir.writeFile(sub_path, data); | ||
| 939 | } else { | ||
| 940 | return dir.symLink(data, sub_path, .{}); | ||
| 941 | } | ||
| 942 | } | ||
| 943 | |||
| 944 | fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { | ||
| 945 | var buf: [1024]u8 = undefined; | ||
| 946 | |||
| 947 | var hasher = hasher_init; | ||
| 948 | while (true) { | ||
| 949 | const bytes_read = try file.read(&buf); | ||
| 950 | if (bytes_read == 0) break; | ||
| 951 | hasher.update(buf[0..bytes_read]); | ||
| 952 | } | ||
| 953 | |||
| 954 | hasher.final(bin_digest); | ||
| 955 | } | ||
| 956 | |||
| 957 | // Create/Write a file, close it, then grab its stat.mtime timestamp. | ||
| 958 | fn testGetCurrentFileTimestamp() !i128 { | ||
| 959 | var file = try fs.cwd().createFile("test-filetimestamp.tmp", .{ | ||
| 960 | .read = true, | ||
| 961 | .truncate = true, | ||
| 962 | }); | ||
| 963 | defer file.close(); | ||
| 964 | |||
| 965 | return (try file.stat()).mtime; | ||
| 966 | } | ||
| 967 | |||
| 968 | test "cache file and then recall it" { | ||
| 969 | if (builtin.os.tag == .wasi) { | ||
| 970 | // https://github.com/ziglang/zig/issues/5437 | ||
| 971 | return error.SkipZigTest; | ||
| 972 | } | ||
| 973 | |||
| 974 | const cwd = fs.cwd(); | ||
| 975 | |||
| 976 | const temp_file = "test.txt"; | ||
| 977 | const temp_manifest_dir = "temp_manifest_dir"; | ||
| 978 | |||
| 979 | try cwd.writeFile(temp_file, "Hello, world!\n"); | ||
| 980 | |||
| 981 | // Wait for file timestamps to tick | ||
| 982 | const initial_time = try testGetCurrentFileTimestamp(); | ||
| 983 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | ||
| 984 | std.time.sleep(1); | ||
| 985 | } | ||
| 986 | |||
| 987 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 988 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 989 | |||
| 990 | { | ||
| 991 | var cache = Cache{ | ||
| 992 | .gpa = testing.allocator, | ||
| 993 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 994 | }; | ||
| 995 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 996 | defer cache.manifest_dir.close(); | ||
| 997 | |||
| 998 | { | ||
| 999 | var ch = cache.obtain(); | ||
| 1000 | defer ch.deinit(); | ||
| 1001 | |||
| 1002 | ch.hash.add(true); | ||
| 1003 | ch.hash.add(@as(u16, 1234)); | ||
| 1004 | ch.hash.addBytes("1234"); | ||
| 1005 | _ = try ch.addFile(temp_file, null); | ||
| 1006 | |||
| 1007 | // There should be nothing in the cache | ||
| 1008 | try testing.expectEqual(false, try ch.hit()); | ||
| 1009 | |||
| 1010 | digest1 = ch.final(); | ||
| 1011 | try ch.writeManifest(); | ||
| 1012 | } | ||
| 1013 | { | ||
| 1014 | var ch = cache.obtain(); | ||
| 1015 | defer ch.deinit(); | ||
| 1016 | |||
| 1017 | ch.hash.add(true); | ||
| 1018 | ch.hash.add(@as(u16, 1234)); | ||
| 1019 | ch.hash.addBytes("1234"); | ||
| 1020 | _ = try ch.addFile(temp_file, null); | ||
| 1021 | |||
| 1022 | // Cache hit! We just "built" the same file | ||
| 1023 | try testing.expect(try ch.hit()); | ||
| 1024 | digest2 = ch.final(); | ||
| 1025 | |||
| 1026 | try testing.expectEqual(false, ch.have_exclusive_lock); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | try testing.expectEqual(digest1, digest2); | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1033 | try cwd.deleteFile(temp_file); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | test "check that changing a file makes cache fail" { | ||
| 1037 | if (builtin.os.tag == .wasi) { | ||
| 1038 | // https://github.com/ziglang/zig/issues/5437 | ||
| 1039 | return error.SkipZigTest; | ||
| 1040 | } | ||
| 1041 | const cwd = fs.cwd(); | ||
| 1042 | |||
| 1043 | const temp_file = "cache_hash_change_file_test.txt"; | ||
| 1044 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; | ||
| 1045 | const original_temp_file_contents = "Hello, world!\n"; | ||
| 1046 | const updated_temp_file_contents = "Hello, world; but updated!\n"; | ||
| 1047 | |||
| 1048 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1049 | try cwd.deleteTree(temp_file); | ||
| 1050 | |||
| 1051 | try cwd.writeFile(temp_file, original_temp_file_contents); | ||
| 1052 | |||
| 1053 | // Wait for file timestamps to tick | ||
| 1054 | const initial_time = try testGetCurrentFileTimestamp(); | ||
| 1055 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | ||
| 1056 | std.time.sleep(1); | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 1060 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 1061 | |||
| 1062 | { | ||
| 1063 | var cache = Cache{ | ||
| 1064 | .gpa = testing.allocator, | ||
| 1065 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 1066 | }; | ||
| 1067 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1068 | defer cache.manifest_dir.close(); | ||
| 1069 | |||
| 1070 | { | ||
| 1071 | var ch = cache.obtain(); | ||
| 1072 | defer ch.deinit(); | ||
| 1073 | |||
| 1074 | ch.hash.addBytes("1234"); | ||
| 1075 | const temp_file_idx = try ch.addFile(temp_file, 100); | ||
| 1076 | |||
| 1077 | // There should be nothing in the cache | ||
| 1078 | try testing.expectEqual(false, try ch.hit()); | ||
| 1079 | |||
| 1080 | try testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?)); | ||
| 1081 | |||
| 1082 | digest1 = ch.final(); | ||
| 1083 | |||
| 1084 | try ch.writeManifest(); | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | try cwd.writeFile(temp_file, updated_temp_file_contents); | ||
| 1088 | |||
| 1089 | { | ||
| 1090 | var ch = cache.obtain(); | ||
| 1091 | defer ch.deinit(); | ||
| 1092 | |||
| 1093 | ch.hash.addBytes("1234"); | ||
| 1094 | const temp_file_idx = try ch.addFile(temp_file, 100); | ||
| 1095 | |||
| 1096 | // A file that we depend on has been updated, so the cache should not contain an entry for it | ||
| 1097 | try testing.expectEqual(false, try ch.hit()); | ||
| 1098 | |||
| 1099 | // The cache system does not keep the contents of re-hashed input files. | ||
| 1100 | try testing.expect(ch.files.items[temp_file_idx].contents == null); | ||
| 1101 | |||
| 1102 | digest2 = ch.final(); | ||
| 1103 | |||
| 1104 | try ch.writeManifest(); | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1111 | try cwd.deleteTree(temp_file); | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | test "no file inputs" { | ||
| 1115 | if (builtin.os.tag == .wasi) { | ||
| 1116 | // https://github.com/ziglang/zig/issues/5437 | ||
| 1117 | return error.SkipZigTest; | ||
| 1118 | } | ||
| 1119 | const cwd = fs.cwd(); | ||
| 1120 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; | ||
| 1121 | defer cwd.deleteTree(temp_manifest_dir) catch {}; | ||
| 1122 | |||
| 1123 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 1124 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 1125 | |||
| 1126 | var cache = Cache{ | ||
| 1127 | .gpa = testing.allocator, | ||
| 1128 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 1129 | }; | ||
| 1130 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1131 | defer cache.manifest_dir.close(); | ||
| 1132 | |||
| 1133 | { | ||
| 1134 | var man = cache.obtain(); | ||
| 1135 | defer man.deinit(); | ||
| 1136 | |||
| 1137 | man.hash.addBytes("1234"); | ||
| 1138 | |||
| 1139 | // There should be nothing in the cache | ||
| 1140 | try testing.expectEqual(false, try man.hit()); | ||
| 1141 | |||
| 1142 | digest1 = man.final(); | ||
| 1143 | |||
| 1144 | try man.writeManifest(); | ||
| 1145 | } | ||
| 1146 | { | ||
| 1147 | var man = cache.obtain(); | ||
| 1148 | defer man.deinit(); | ||
| 1149 | |||
| 1150 | man.hash.addBytes("1234"); | ||
| 1151 | |||
| 1152 | try testing.expect(try man.hit()); | ||
| 1153 | digest2 = man.final(); | ||
| 1154 | try testing.expectEqual(false, man.have_exclusive_lock); | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | try testing.expectEqual(digest1, digest2); | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | test "Manifest with files added after initial hash work" { | ||
| 1161 | if (builtin.os.tag == .wasi) { | ||
| 1162 | // https://github.com/ziglang/zig/issues/5437 | ||
| 1163 | return error.SkipZigTest; | ||
| 1164 | } | ||
| 1165 | const cwd = fs.cwd(); | ||
| 1166 | |||
| 1167 | const temp_file1 = "cache_hash_post_file_test1.txt"; | ||
| 1168 | const temp_file2 = "cache_hash_post_file_test2.txt"; | ||
| 1169 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; | ||
| 1170 | |||
| 1171 | try cwd.writeFile(temp_file1, "Hello, world!\n"); | ||
| 1172 | try cwd.writeFile(temp_file2, "Hello world the second!\n"); | ||
| 1173 | |||
| 1174 | // Wait for file timestamps to tick | ||
| 1175 | const initial_time = try testGetCurrentFileTimestamp(); | ||
| 1176 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | ||
| 1177 | std.time.sleep(1); | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 1181 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 1182 | var digest3: [hex_digest_len]u8 = undefined; | ||
| 1183 | |||
| 1184 | { | ||
| 1185 | var cache = Cache{ | ||
| 1186 | .gpa = testing.allocator, | ||
| 1187 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 1188 | }; | ||
| 1189 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1190 | defer cache.manifest_dir.close(); | ||
| 1191 | |||
| 1192 | { | ||
| 1193 | var ch = cache.obtain(); | ||
| 1194 | defer ch.deinit(); | ||
| 1195 | |||
| 1196 | ch.hash.addBytes("1234"); | ||
| 1197 | _ = try ch.addFile(temp_file1, null); | ||
| 1198 | |||
| 1199 | // There should be nothing in the cache | ||
| 1200 | try testing.expectEqual(false, try ch.hit()); | ||
| 1201 | |||
| 1202 | _ = try ch.addFilePost(temp_file2); | ||
| 1203 | |||
| 1204 | digest1 = ch.final(); | ||
| 1205 | try ch.writeManifest(); | ||
| 1206 | } | ||
| 1207 | { | ||
| 1208 | var ch = cache.obtain(); | ||
| 1209 | defer ch.deinit(); | ||
| 1210 | |||
| 1211 | ch.hash.addBytes("1234"); | ||
| 1212 | _ = try ch.addFile(temp_file1, null); | ||
| 1213 | |||
| 1214 | try testing.expect(try ch.hit()); | ||
| 1215 | digest2 = ch.final(); | ||
| 1216 | |||
| 1217 | try testing.expectEqual(false, ch.have_exclusive_lock); | ||
| 1218 | } | ||
| 1219 | try testing.expect(mem.eql(u8, &digest1, &digest2)); | ||
| 1220 | |||
| 1221 | // Modify the file added after initial hash | ||
| 1222 | try cwd.writeFile(temp_file2, "Hello world the second, updated\n"); | ||
| 1223 | |||
| 1224 | // Wait for file timestamps to tick | ||
| 1225 | const initial_time2 = try testGetCurrentFileTimestamp(); | ||
| 1226 | while ((try testGetCurrentFileTimestamp()) == initial_time2) { | ||
| 1227 | std.time.sleep(1); | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | { | ||
| 1231 | var ch = cache.obtain(); | ||
| 1232 | defer ch.deinit(); | ||
| 1233 | |||
| 1234 | ch.hash.addBytes("1234"); | ||
| 1235 | _ = try ch.addFile(temp_file1, null); | ||
| 1236 | |||
| 1237 | // A file that we depend on has been updated, so the cache should not contain an entry for it | ||
| 1238 | try testing.expectEqual(false, try ch.hit()); | ||
| 1239 | |||
| 1240 | _ = try ch.addFilePost(temp_file2); | ||
| 1241 | |||
| 1242 | digest3 = ch.final(); | ||
| 1243 | |||
| 1244 | try ch.writeManifest(); | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1251 | try cwd.deleteFile(temp_file1); | ||
| 1252 | try cwd.deleteFile(temp_file2); | ||
| 1253 | } | ||
lib/std/Build/Cache/DepTokenizer.zig created+1069| ... | @@ -0,0 +1,1069 @@ | ||
| 1 | const Tokenizer = @This(); | ||
| 2 | |||
| 3 | index: usize = 0, | ||
| 4 | bytes: []const u8, | ||
| 5 | state: State = .lhs, | ||
| 6 | |||
| 7 | const std = @import("std"); | ||
| 8 | const testing = std.testing; | ||
| 9 | const assert = std.debug.assert; | ||
| 10 | |||
| 11 | pub fn next(self: *Tokenizer) ?Token { | ||
| 12 | var start = self.index; | ||
| 13 | var must_resolve = false; | ||
| 14 | while (self.index < self.bytes.len) { | ||
| 15 | const char = self.bytes[self.index]; | ||
| 16 | switch (self.state) { | ||
| 17 | .lhs => switch (char) { | ||
| 18 | '\t', '\n', '\r', ' ' => { | ||
| 19 | // silently ignore whitespace | ||
| 20 | self.index += 1; | ||
| 21 | }, | ||
| 22 | else => { | ||
| 23 | start = self.index; | ||
| 24 | self.state = .target; | ||
| 25 | }, | ||
| 26 | }, | ||
| 27 | .target => switch (char) { | ||
| 28 | '\t', '\n', '\r', ' ' => { | ||
| 29 | return errorIllegalChar(.invalid_target, self.index, char); | ||
| 30 | }, | ||
| 31 | '$' => { | ||
| 32 | self.state = .target_dollar_sign; | ||
| 33 | self.index += 1; | ||
| 34 | }, | ||
| 35 | '\\' => { | ||
| 36 | self.state = .target_reverse_solidus; | ||
| 37 | self.index += 1; | ||
| 38 | }, | ||
| 39 | ':' => { | ||
| 40 | self.state = .target_colon; | ||
| 41 | self.index += 1; | ||
| 42 | }, | ||
| 43 | else => { | ||
| 44 | self.index += 1; | ||
| 45 | }, | ||
| 46 | }, | ||
| 47 | .target_reverse_solidus => switch (char) { | ||
| 48 | '\t', '\n', '\r' => { | ||
| 49 | return errorIllegalChar(.bad_target_escape, self.index, char); | ||
| 50 | }, | ||
| 51 | ' ', '#', '\\' => { | ||
| 52 | must_resolve = true; | ||
| 53 | self.state = .target; | ||
| 54 | self.index += 1; | ||
| 55 | }, | ||
| 56 | '$' => { | ||
| 57 | self.state = .target_dollar_sign; | ||
| 58 | self.index += 1; | ||
| 59 | }, | ||
| 60 | else => { | ||
| 61 | self.state = .target; | ||
| 62 | self.index += 1; | ||
| 63 | }, | ||
| 64 | }, | ||
| 65 | .target_dollar_sign => switch (char) { | ||
| 66 | '$' => { | ||
| 67 | must_resolve = true; | ||
| 68 | self.state = .target; | ||
| 69 | self.index += 1; | ||
| 70 | }, | ||
| 71 | else => { | ||
| 72 | return errorIllegalChar(.expected_dollar_sign, self.index, char); | ||
| 73 | }, | ||
| 74 | }, | ||
| 75 | .target_colon => switch (char) { | ||
| 76 | '\n', '\r' => { | ||
| 77 | const bytes = self.bytes[start .. self.index - 1]; | ||
| 78 | if (bytes.len != 0) { | ||
| 79 | self.state = .lhs; | ||
| 80 | return finishTarget(must_resolve, bytes); | ||
| 81 | } | ||
| 82 | // silently ignore null target | ||
| 83 | self.state = .lhs; | ||
| 84 | }, | ||
| 85 | '/', '\\' => { | ||
| 86 | self.state = .target_colon_reverse_solidus; | ||
| 87 | self.index += 1; | ||
| 88 | }, | ||
| 89 | else => { | ||
| 90 | const bytes = self.bytes[start .. self.index - 1]; | ||
| 91 | if (bytes.len != 0) { | ||
| 92 | self.state = .rhs; | ||
| 93 | return finishTarget(must_resolve, bytes); | ||
| 94 | } | ||
| 95 | // silently ignore null target | ||
| 96 | self.state = .lhs; | ||
| 97 | }, | ||
| 98 | }, | ||
| 99 | .target_colon_reverse_solidus => switch (char) { | ||
| 100 | '\n', '\r' => { | ||
| 101 | const bytes = self.bytes[start .. self.index - 2]; | ||
| 102 | if (bytes.len != 0) { | ||
| 103 | self.state = .lhs; | ||
| 104 | return finishTarget(must_resolve, bytes); | ||
| 105 | } | ||
| 106 | // silently ignore null target | ||
| 107 | self.state = .lhs; | ||
| 108 | }, | ||
| 109 | else => { | ||
| 110 | self.state = .target; | ||
| 111 | }, | ||
| 112 | }, | ||
| 113 | .rhs => switch (char) { | ||
| 114 | '\t', ' ' => { | ||
| 115 | // silently ignore horizontal whitespace | ||
| 116 | self.index += 1; | ||
| 117 | }, | ||
| 118 | '\n', '\r' => { | ||
| 119 | self.state = .lhs; | ||
| 120 | }, | ||
| 121 | '\\' => { | ||
| 122 | self.state = .rhs_continuation; | ||
| 123 | self.index += 1; | ||
| 124 | }, | ||
| 125 | '"' => { | ||
| 126 | self.state = .prereq_quote; | ||
| 127 | self.index += 1; | ||
| 128 | start = self.index; | ||
| 129 | }, | ||
| 130 | else => { | ||
| 131 | start = self.index; | ||
| 132 | self.state = .prereq; | ||
| 133 | }, | ||
| 134 | }, | ||
| 135 | .rhs_continuation => switch (char) { | ||
| 136 | '\n' => { | ||
| 137 | self.state = .rhs; | ||
| 138 | self.index += 1; | ||
| 139 | }, | ||
| 140 | '\r' => { | ||
| 141 | self.state = .rhs_continuation_linefeed; | ||
| 142 | self.index += 1; | ||
| 143 | }, | ||
| 144 | else => { | ||
| 145 | return errorIllegalChar(.continuation_eol, self.index, char); | ||
| 146 | }, | ||
| 147 | }, | ||
| 148 | .rhs_continuation_linefeed => switch (char) { | ||
| 149 | '\n' => { | ||
| 150 | self.state = .rhs; | ||
| 151 | self.index += 1; | ||
| 152 | }, | ||
| 153 | else => { | ||
| 154 | return errorIllegalChar(.continuation_eol, self.index, char); | ||
| 155 | }, | ||
| 156 | }, | ||
| 157 | .prereq_quote => switch (char) { | ||
| 158 | '"' => { | ||
| 159 | self.index += 1; | ||
| 160 | self.state = .rhs; | ||
| 161 | return Token{ .prereq = self.bytes[start .. self.index - 1] }; | ||
| 162 | }, | ||
| 163 | else => { | ||
| 164 | self.index += 1; | ||
| 165 | }, | ||
| 166 | }, | ||
| 167 | .prereq => switch (char) { | ||
| 168 | '\t', ' ' => { | ||
| 169 | self.state = .rhs; | ||
| 170 | return Token{ .prereq = self.bytes[start..self.index] }; | ||
| 171 | }, | ||
| 172 | '\n', '\r' => { | ||
| 173 | self.state = .lhs; | ||
| 174 | return Token{ .prereq = self.bytes[start..self.index] }; | ||
| 175 | }, | ||
| 176 | '\\' => { | ||
| 177 | self.state = .prereq_continuation; | ||
| 178 | self.index += 1; | ||
| 179 | }, | ||
| 180 | else => { | ||
| 181 | self.index += 1; | ||
| 182 | }, | ||
| 183 | }, | ||
| 184 | .prereq_continuation => switch (char) { | ||
| 185 | '\n' => { | ||
| 186 | self.index += 1; | ||
| 187 | self.state = .rhs; | ||
| 188 | return Token{ .prereq = self.bytes[start .. self.index - 2] }; | ||
| 189 | }, | ||
| 190 | '\r' => { | ||
| 191 | self.state = .prereq_continuation_linefeed; | ||
| 192 | self.index += 1; | ||
| 193 | }, | ||
| 194 | else => { | ||
| 195 | // not continuation | ||
| 196 | self.state = .prereq; | ||
| 197 | self.index += 1; | ||
| 198 | }, | ||
| 199 | }, | ||
| 200 | .prereq_continuation_linefeed => switch (char) { | ||
| 201 | '\n' => { | ||
| 202 | self.index += 1; | ||
| 203 | self.state = .rhs; | ||
| 204 | return Token{ .prereq = self.bytes[start .. self.index - 1] }; | ||
| 205 | }, | ||
| 206 | else => { | ||
| 207 | return errorIllegalChar(.continuation_eol, self.index, char); | ||
| 208 | }, | ||
| 209 | }, | ||
| 210 | } | ||
| 211 | } else { | ||
| 212 | switch (self.state) { | ||
| 213 | .lhs, | ||
| 214 | .rhs, | ||
| 215 | .rhs_continuation, | ||
| 216 | .rhs_continuation_linefeed, | ||
| 217 | => return null, | ||
| 218 | .target => { | ||
| 219 | return errorPosition(.incomplete_target, start, self.bytes[start..]); | ||
| 220 | }, | ||
| 221 | .target_reverse_solidus, | ||
| 222 | .target_dollar_sign, | ||
| 223 | => { | ||
| 224 | const idx = self.index - 1; | ||
| 225 | return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]); | ||
| 226 | }, | ||
| 227 | .target_colon => { | ||
| 228 | const bytes = self.bytes[start .. self.index - 1]; | ||
| 229 | if (bytes.len != 0) { | ||
| 230 | self.index += 1; | ||
| 231 | self.state = .rhs; | ||
| 232 | return finishTarget(must_resolve, bytes); | ||
| 233 | } | ||
| 234 | // silently ignore null target | ||
| 235 | self.state = .lhs; | ||
| 236 | return null; | ||
| 237 | }, | ||
| 238 | .target_colon_reverse_solidus => { | ||
| 239 | const bytes = self.bytes[start .. self.index - 2]; | ||
| 240 | if (bytes.len != 0) { | ||
| 241 | self.index += 1; | ||
| 242 | self.state = .rhs; | ||
| 243 | return finishTarget(must_resolve, bytes); | ||
| 244 | } | ||
| 245 | // silently ignore null target | ||
| 246 | self.state = .lhs; | ||
| 247 | return null; | ||
| 248 | }, | ||
| 249 | .prereq_quote => { | ||
| 250 | return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]); | ||
| 251 | }, | ||
| 252 | .prereq => { | ||
| 253 | self.state = .lhs; | ||
| 254 | return Token{ .prereq = self.bytes[start..] }; | ||
| 255 | }, | ||
| 256 | .prereq_continuation => { | ||
| 257 | self.state = .lhs; | ||
| 258 | return Token{ .prereq = self.bytes[start .. self.index - 1] }; | ||
| 259 | }, | ||
| 260 | .prereq_continuation_linefeed => { | ||
| 261 | self.state = .lhs; | ||
| 262 | return Token{ .prereq = self.bytes[start .. self.index - 2] }; | ||
| 263 | }, | ||
| 264 | } | ||
| 265 | } | ||
| 266 | unreachable; | ||
| 267 | } | ||
| 268 | |||
| 269 | fn errorPosition(comptime id: std.meta.Tag(Token), index: usize, bytes: []const u8) Token { | ||
| 270 | return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes }); | ||
| 271 | } | ||
| 272 | |||
| 273 | fn errorIllegalChar(comptime id: std.meta.Tag(Token), index: usize, char: u8) Token { | ||
| 274 | return @unionInit(Token, @tagName(id), .{ .index = index, .char = char }); | ||
| 275 | } | ||
| 276 | |||
| 277 | fn finishTarget(must_resolve: bool, bytes: []const u8) Token { | ||
| 278 | return if (must_resolve) .{ .target_must_resolve = bytes } else .{ .target = bytes }; | ||
| 279 | } | ||
| 280 | |||
| 281 | const State = enum { | ||
| 282 | lhs, | ||
| 283 | target, | ||
| 284 | target_reverse_solidus, | ||
| 285 | target_dollar_sign, | ||
| 286 | target_colon, | ||
| 287 | target_colon_reverse_solidus, | ||
| 288 | rhs, | ||
| 289 | rhs_continuation, | ||
| 290 | rhs_continuation_linefeed, | ||
| 291 | prereq_quote, | ||
| 292 | prereq, | ||
| 293 | prereq_continuation, | ||
| 294 | prereq_continuation_linefeed, | ||
| 295 | }; | ||
| 296 | |||
| 297 | pub const Token = union(enum) { | ||
| 298 | target: []const u8, | ||
| 299 | target_must_resolve: []const u8, | ||
| 300 | prereq: []const u8, | ||
| 301 | |||
| 302 | incomplete_quoted_prerequisite: IndexAndBytes, | ||
| 303 | incomplete_target: IndexAndBytes, | ||
| 304 | |||
| 305 | invalid_target: IndexAndChar, | ||
| 306 | bad_target_escape: IndexAndChar, | ||
| 307 | expected_dollar_sign: IndexAndChar, | ||
| 308 | continuation_eol: IndexAndChar, | ||
| 309 | incomplete_escape: IndexAndChar, | ||
| 310 | |||
| 311 | pub const IndexAndChar = struct { | ||
| 312 | index: usize, | ||
| 313 | char: u8, | ||
| 314 | }; | ||
| 315 | |||
| 316 | pub const IndexAndBytes = struct { | ||
| 317 | index: usize, | ||
| 318 | bytes: []const u8, | ||
| 319 | }; | ||
| 320 | |||
| 321 | /// Resolve escapes in target. Only valid with .target_must_resolve. | ||
| 322 | pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void { | ||
| 323 | const bytes = self.target_must_resolve; // resolve called on incorrect token | ||
| 324 | |||
| 325 | var state: enum { start, escape, dollar } = .start; | ||
| 326 | for (bytes) |c| { | ||
| 327 | switch (state) { | ||
| 328 | .start => { | ||
| 329 | switch (c) { | ||
| 330 | '\\' => state = .escape, | ||
| 331 | '$' => state = .dollar, | ||
| 332 | else => try writer.writeByte(c), | ||
| 333 | } | ||
| 334 | }, | ||
| 335 | .escape => { | ||
| 336 | switch (c) { | ||
| 337 | ' ', '#', '\\' => {}, | ||
| 338 | '$' => { | ||
| 339 | try writer.writeByte('\\'); | ||
| 340 | state = .dollar; | ||
| 341 | continue; | ||
| 342 | }, | ||
| 343 | else => try writer.writeByte('\\'), | ||
| 344 | } | ||
| 345 | try writer.writeByte(c); | ||
| 346 | state = .start; | ||
| 347 | }, | ||
| 348 | .dollar => { | ||
| 349 | try writer.writeByte('$'); | ||
| 350 | switch (c) { | ||
| 351 | '$' => {}, | ||
| 352 | else => try writer.writeByte(c), | ||
| 353 | } | ||
| 354 | state = .start; | ||
| 355 | }, | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void { | ||
| 361 | switch (self) { | ||
| 362 | .target, .target_must_resolve, .prereq => unreachable, // not an error | ||
| 363 | .incomplete_quoted_prerequisite, | ||
| 364 | .incomplete_target, | ||
| 365 | => |index_and_bytes| { | ||
| 366 | try writer.print("{s} '", .{self.errStr()}); | ||
| 367 | if (self == .incomplete_target) { | ||
| 368 | const tmp = Token{ .target_must_resolve = index_and_bytes.bytes }; | ||
| 369 | try tmp.resolve(writer); | ||
| 370 | } else { | ||
| 371 | try printCharValues(writer, index_and_bytes.bytes); | ||
| 372 | } | ||
| 373 | try writer.print("' at position {d}", .{index_and_bytes.index}); | ||
| 374 | }, | ||
| 375 | .invalid_target, | ||
| 376 | .bad_target_escape, | ||
| 377 | .expected_dollar_sign, | ||
| 378 | .continuation_eol, | ||
| 379 | .incomplete_escape, | ||
| 380 | => |index_and_char| { | ||
| 381 | try writer.writeAll("illegal char "); | ||
| 382 | try printUnderstandableChar(writer, index_and_char.char); | ||
| 383 | try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() }); | ||
| 384 | }, | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | fn errStr(self: Token) []const u8 { | ||
| 389 | return switch (self) { | ||
| 390 | .target, .target_must_resolve, .prereq => unreachable, // not an error | ||
| 391 | .incomplete_quoted_prerequisite => "incomplete quoted prerequisite", | ||
| 392 | .incomplete_target => "incomplete target", | ||
| 393 | .invalid_target => "invalid target", | ||
| 394 | .bad_target_escape => "bad target escape", | ||
| 395 | .expected_dollar_sign => "expecting '$'", | ||
| 396 | .continuation_eol => "continuation expecting end-of-line", | ||
| 397 | .incomplete_escape => "incomplete escape", | ||
| 398 | }; | ||
| 399 | } | ||
| 400 | }; | ||
| 401 | |||
| 402 | test "empty file" { | ||
| 403 | try depTokenizer("", ""); | ||
| 404 | } | ||
| 405 | |||
| 406 | test "empty whitespace" { | ||
| 407 | try depTokenizer("\n", ""); | ||
| 408 | try depTokenizer("\r", ""); | ||
| 409 | try depTokenizer("\r\n", ""); | ||
| 410 | try depTokenizer(" ", ""); | ||
| 411 | } | ||
| 412 | |||
| 413 | test "empty colon" { | ||
| 414 | try depTokenizer(":", ""); | ||
| 415 | try depTokenizer("\n:", ""); | ||
| 416 | try depTokenizer("\r:", ""); | ||
| 417 | try depTokenizer("\r\n:", ""); | ||
| 418 | try depTokenizer(" :", ""); | ||
| 419 | } | ||
| 420 | |||
| 421 | test "empty target" { | ||
| 422 | try depTokenizer("foo.o:", "target = {foo.o}"); | ||
| 423 | try depTokenizer( | ||
| 424 | \\foo.o: | ||
| 425 | \\bar.o: | ||
| 426 | \\abcd.o: | ||
| 427 | , | ||
| 428 | \\target = {foo.o} | ||
| 429 | \\target = {bar.o} | ||
| 430 | \\target = {abcd.o} | ||
| 431 | ); | ||
| 432 | } | ||
| 433 | |||
| 434 | test "whitespace empty target" { | ||
| 435 | try depTokenizer("\nfoo.o:", "target = {foo.o}"); | ||
| 436 | try depTokenizer("\rfoo.o:", "target = {foo.o}"); | ||
| 437 | try depTokenizer("\r\nfoo.o:", "target = {foo.o}"); | ||
| 438 | try depTokenizer(" foo.o:", "target = {foo.o}"); | ||
| 439 | } | ||
| 440 | |||
| 441 | test "escape empty target" { | ||
| 442 | try depTokenizer("\\ foo.o:", "target = { foo.o}"); | ||
| 443 | try depTokenizer("\\#foo.o:", "target = {#foo.o}"); | ||
| 444 | try depTokenizer("\\\\foo.o:", "target = {\\foo.o}"); | ||
| 445 | try depTokenizer("$$foo.o:", "target = {$foo.o}"); | ||
| 446 | } | ||
| 447 | |||
| 448 | test "empty target linefeeds" { | ||
| 449 | try depTokenizer("\n", ""); | ||
| 450 | try depTokenizer("\r\n", ""); | ||
| 451 | |||
| 452 | const expect = "target = {foo.o}"; | ||
| 453 | try depTokenizer( | ||
| 454 | \\foo.o: | ||
| 455 | , expect); | ||
| 456 | try depTokenizer( | ||
| 457 | \\foo.o: | ||
| 458 | \\ | ||
| 459 | , expect); | ||
| 460 | try depTokenizer( | ||
| 461 | \\foo.o: | ||
| 462 | , expect); | ||
| 463 | try depTokenizer( | ||
| 464 | \\foo.o: | ||
| 465 | \\ | ||
| 466 | , expect); | ||
| 467 | } | ||
| 468 | |||
| 469 | test "empty target linefeeds + continuations" { | ||
| 470 | const expect = "target = {foo.o}"; | ||
| 471 | try depTokenizer( | ||
| 472 | \\foo.o:\ | ||
| 473 | , expect); | ||
| 474 | try depTokenizer( | ||
| 475 | \\foo.o:\ | ||
| 476 | \\ | ||
| 477 | , expect); | ||
| 478 | try depTokenizer( | ||
| 479 | \\foo.o:\ | ||
| 480 | , expect); | ||
| 481 | try depTokenizer( | ||
| 482 | \\foo.o:\ | ||
| 483 | \\ | ||
| 484 | , expect); | ||
| 485 | } | ||
| 486 | |||
| 487 | test "empty target linefeeds + hspace + continuations" { | ||
| 488 | const expect = "target = {foo.o}"; | ||
| 489 | try depTokenizer( | ||
| 490 | \\foo.o: \ | ||
| 491 | , expect); | ||
| 492 | try depTokenizer( | ||
| 493 | \\foo.o: \ | ||
| 494 | \\ | ||
| 495 | , expect); | ||
| 496 | try depTokenizer( | ||
| 497 | \\foo.o: \ | ||
| 498 | , expect); | ||
| 499 | try depTokenizer( | ||
| 500 | \\foo.o: \ | ||
| 501 | \\ | ||
| 502 | , expect); | ||
| 503 | } | ||
| 504 | |||
| 505 | test "prereq" { | ||
| 506 | const expect = | ||
| 507 | \\target = {foo.o} | ||
| 508 | \\prereq = {foo.c} | ||
| 509 | ; | ||
| 510 | try depTokenizer("foo.o: foo.c", expect); | ||
| 511 | try depTokenizer( | ||
| 512 | \\foo.o: \ | ||
| 513 | \\foo.c | ||
| 514 | , expect); | ||
| 515 | try depTokenizer( | ||
| 516 | \\foo.o: \ | ||
| 517 | \\ foo.c | ||
| 518 | , expect); | ||
| 519 | try depTokenizer( | ||
| 520 | \\foo.o: \ | ||
| 521 | \\ foo.c | ||
| 522 | , expect); | ||
| 523 | } | ||
| 524 | |||
| 525 | test "prereq continuation" { | ||
| 526 | const expect = | ||
| 527 | \\target = {foo.o} | ||
| 528 | \\prereq = {foo.h} | ||
| 529 | \\prereq = {bar.h} | ||
| 530 | ; | ||
| 531 | try depTokenizer( | ||
| 532 | \\foo.o: foo.h\ | ||
| 533 | \\bar.h | ||
| 534 | , expect); | ||
| 535 | try depTokenizer( | ||
| 536 | \\foo.o: foo.h\ | ||
| 537 | \\bar.h | ||
| 538 | , expect); | ||
| 539 | } | ||
| 540 | |||
| 541 | test "multiple prereqs" { | ||
| 542 | const expect = | ||
| 543 | \\target = {foo.o} | ||
| 544 | \\prereq = {foo.c} | ||
| 545 | \\prereq = {foo.h} | ||
| 546 | \\prereq = {bar.h} | ||
| 547 | ; | ||
| 548 | try depTokenizer("foo.o: foo.c foo.h bar.h", expect); | ||
| 549 | try depTokenizer( | ||
| 550 | \\foo.o: \ | ||
| 551 | \\foo.c foo.h bar.h | ||
| 552 | , expect); | ||
| 553 | try depTokenizer( | ||
| 554 | \\foo.o: foo.c foo.h bar.h\ | ||
| 555 | , expect); | ||
| 556 | try depTokenizer( | ||
| 557 | \\foo.o: foo.c foo.h bar.h\ | ||
| 558 | \\ | ||
| 559 | , expect); | ||
| 560 | try depTokenizer( | ||
| 561 | \\foo.o: \ | ||
| 562 | \\foo.c \ | ||
| 563 | \\ foo.h\ | ||
| 564 | \\bar.h | ||
| 565 | \\ | ||
| 566 | , expect); | ||
| 567 | try depTokenizer( | ||
| 568 | \\foo.o: \ | ||
| 569 | \\foo.c \ | ||
| 570 | \\ foo.h\ | ||
| 571 | \\bar.h\ | ||
| 572 | \\ | ||
| 573 | , expect); | ||
| 574 | try depTokenizer( | ||
| 575 | \\foo.o: \ | ||
| 576 | \\foo.c \ | ||
| 577 | \\ foo.h\ | ||
| 578 | \\bar.h\ | ||
| 579 | , expect); | ||
| 580 | } | ||
| 581 | |||
| 582 | test "multiple targets and prereqs" { | ||
| 583 | try depTokenizer( | ||
| 584 | \\foo.o: foo.c | ||
| 585 | \\bar.o: bar.c a.h b.h c.h | ||
| 586 | \\abc.o: abc.c \ | ||
| 587 | \\ one.h two.h \ | ||
| 588 | \\ three.h four.h | ||
| 589 | , | ||
| 590 | \\target = {foo.o} | ||
| 591 | \\prereq = {foo.c} | ||
| 592 | \\target = {bar.o} | ||
| 593 | \\prereq = {bar.c} | ||
| 594 | \\prereq = {a.h} | ||
| 595 | \\prereq = {b.h} | ||
| 596 | \\prereq = {c.h} | ||
| 597 | \\target = {abc.o} | ||
| 598 | \\prereq = {abc.c} | ||
| 599 | \\prereq = {one.h} | ||
| 600 | \\prereq = {two.h} | ||
| 601 | \\prereq = {three.h} | ||
| 602 | \\prereq = {four.h} | ||
| 603 | ); | ||
| 604 | try depTokenizer( | ||
| 605 | \\ascii.o: ascii.c | ||
| 606 | \\base64.o: base64.c stdio.h | ||
| 607 | \\elf.o: elf.c a.h b.h c.h | ||
| 608 | \\macho.o: \ | ||
| 609 | \\ macho.c\ | ||
| 610 | \\ a.h b.h c.h | ||
| 611 | , | ||
| 612 | \\target = {ascii.o} | ||
| 613 | \\prereq = {ascii.c} | ||
| 614 | \\target = {base64.o} | ||
| 615 | \\prereq = {base64.c} | ||
| 616 | \\prereq = {stdio.h} | ||
| 617 | \\target = {elf.o} | ||
| 618 | \\prereq = {elf.c} | ||
| 619 | \\prereq = {a.h} | ||
| 620 | \\prereq = {b.h} | ||
| 621 | \\prereq = {c.h} | ||
| 622 | \\target = {macho.o} | ||
| 623 | \\prereq = {macho.c} | ||
| 624 | \\prereq = {a.h} | ||
| 625 | \\prereq = {b.h} | ||
| 626 | \\prereq = {c.h} | ||
| 627 | ); | ||
| 628 | try depTokenizer( | ||
| 629 | \\a$$scii.o: ascii.c | ||
| 630 | \\\\base64.o: "\base64.c" "s t#dio.h" | ||
| 631 | \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$" | ||
| 632 | \\macho.o: \ | ||
| 633 | \\ "macho!.c" \ | ||
| 634 | \\ a.h b.h c.h | ||
| 635 | , | ||
| 636 | \\target = {a$scii.o} | ||
| 637 | \\prereq = {ascii.c} | ||
| 638 | \\target = {\base64.o} | ||
| 639 | \\prereq = {\base64.c} | ||
| 640 | \\prereq = {s t#dio.h} | ||
| 641 | \\target = {e\lf.o} | ||
| 642 | \\prereq = {e\lf.c} | ||
| 643 | \\prereq = {a.h$$} | ||
| 644 | \\prereq = {$$b.h c.h$$} | ||
| 645 | \\target = {macho.o} | ||
| 646 | \\prereq = {macho!.c} | ||
| 647 | \\prereq = {a.h} | ||
| 648 | \\prereq = {b.h} | ||
| 649 | \\prereq = {c.h} | ||
| 650 | ); | ||
| 651 | } | ||
| 652 | |||
| 653 | test "windows quoted prereqs" { | ||
| 654 | try depTokenizer( | ||
| 655 | \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c" | ||
| 656 | \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \ | ||
| 657 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \ | ||
| 658 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h" | ||
| 659 | , | ||
| 660 | \\target = {c:\foo.o} | ||
| 661 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c} | ||
| 662 | \\target = {c:\foo2.o} | ||
| 663 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c} | ||
| 664 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h} | ||
| 665 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h} | ||
| 666 | ); | ||
| 667 | } | ||
| 668 | |||
| 669 | test "windows mixed prereqs" { | ||
| 670 | try depTokenizer( | ||
| 671 | \\cimport.o: \ | ||
| 672 | \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \ | ||
| 673 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \ | ||
| 674 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \ | ||
| 675 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \ | ||
| 676 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \ | ||
| 677 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \ | ||
| 678 | \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \ | ||
| 679 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \ | ||
| 680 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \ | ||
| 681 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \ | ||
| 682 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \ | ||
| 683 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \ | ||
| 684 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \ | ||
| 685 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \ | ||
| 686 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \ | ||
| 687 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h" | ||
| 688 | , | ||
| 689 | \\target = {cimport.o} | ||
| 690 | \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h} | ||
| 691 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h} | ||
| 692 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h} | ||
| 693 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h} | ||
| 694 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h} | ||
| 695 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h} | ||
| 696 | \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h} | ||
| 697 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h} | ||
| 698 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h} | ||
| 699 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h} | ||
| 700 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h} | ||
| 701 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h} | ||
| 702 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h} | ||
| 703 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h} | ||
| 704 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h} | ||
| 705 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h} | ||
| 706 | ); | ||
| 707 | } | ||
| 708 | |||
| 709 | test "windows funky targets" { | ||
| 710 | try depTokenizer( | ||
| 711 | \\C:\Users\anon\foo.o: | ||
| 712 | \\C:\Users\anon\foo\ .o: | ||
| 713 | \\C:\Users\anon\foo\#.o: | ||
| 714 | \\C:\Users\anon\foo$$.o: | ||
| 715 | \\C:\Users\anon\\\ foo.o: | ||
| 716 | \\C:\Users\anon\\#foo.o: | ||
| 717 | \\C:\Users\anon\$$foo.o: | ||
| 718 | \\C:\Users\anon\\\ \ \ \ \ foo.o: | ||
| 719 | , | ||
| 720 | \\target = {C:\Users\anon\foo.o} | ||
| 721 | \\target = {C:\Users\anon\foo .o} | ||
| 722 | \\target = {C:\Users\anon\foo#.o} | ||
| 723 | \\target = {C:\Users\anon\foo$.o} | ||
| 724 | \\target = {C:\Users\anon\ foo.o} | ||
| 725 | \\target = {C:\Users\anon\#foo.o} | ||
| 726 | \\target = {C:\Users\anon\$foo.o} | ||
| 727 | \\target = {C:\Users\anon\ foo.o} | ||
| 728 | ); | ||
| 729 | } | ||
| 730 | |||
| 731 | test "windows drive and forward slashes" { | ||
| 732 | try depTokenizer( | ||
| 733 | \\C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj: \ | ||
| 734 | \\ C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c | ||
| 735 | , | ||
| 736 | \\target = {C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj} | ||
| 737 | \\prereq = {C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c} | ||
| 738 | ); | ||
| 739 | } | ||
| 740 | |||
| 741 | test "error incomplete escape - reverse_solidus" { | ||
| 742 | try depTokenizer("\\", | ||
| 743 | \\ERROR: illegal char '\' at position 0: incomplete escape | ||
| 744 | ); | ||
| 745 | try depTokenizer("\t\\", | ||
| 746 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 747 | ); | ||
| 748 | try depTokenizer("\n\\", | ||
| 749 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 750 | ); | ||
| 751 | try depTokenizer("\r\\", | ||
| 752 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 753 | ); | ||
| 754 | try depTokenizer("\r\n\\", | ||
| 755 | \\ERROR: illegal char '\' at position 2: incomplete escape | ||
| 756 | ); | ||
| 757 | try depTokenizer(" \\", | ||
| 758 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 759 | ); | ||
| 760 | } | ||
| 761 | |||
| 762 | test "error incomplete escape - dollar_sign" { | ||
| 763 | try depTokenizer("$", | ||
| 764 | \\ERROR: illegal char '$' at position 0: incomplete escape | ||
| 765 | ); | ||
| 766 | try depTokenizer("\t$", | ||
| 767 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 768 | ); | ||
| 769 | try depTokenizer("\n$", | ||
| 770 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 771 | ); | ||
| 772 | try depTokenizer("\r$", | ||
| 773 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 774 | ); | ||
| 775 | try depTokenizer("\r\n$", | ||
| 776 | \\ERROR: illegal char '$' at position 2: incomplete escape | ||
| 777 | ); | ||
| 778 | try depTokenizer(" $", | ||
| 779 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 780 | ); | ||
| 781 | } | ||
| 782 | |||
| 783 | test "error incomplete target" { | ||
| 784 | try depTokenizer("foo.o", | ||
| 785 | \\ERROR: incomplete target 'foo.o' at position 0 | ||
| 786 | ); | ||
| 787 | try depTokenizer("\tfoo.o", | ||
| 788 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 789 | ); | ||
| 790 | try depTokenizer("\nfoo.o", | ||
| 791 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 792 | ); | ||
| 793 | try depTokenizer("\rfoo.o", | ||
| 794 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 795 | ); | ||
| 796 | try depTokenizer("\r\nfoo.o", | ||
| 797 | \\ERROR: incomplete target 'foo.o' at position 2 | ||
| 798 | ); | ||
| 799 | try depTokenizer(" foo.o", | ||
| 800 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 801 | ); | ||
| 802 | |||
| 803 | try depTokenizer("\\ foo.o", | ||
| 804 | \\ERROR: incomplete target ' foo.o' at position 0 | ||
| 805 | ); | ||
| 806 | try depTokenizer("\\#foo.o", | ||
| 807 | \\ERROR: incomplete target '#foo.o' at position 0 | ||
| 808 | ); | ||
| 809 | try depTokenizer("\\\\foo.o", | ||
| 810 | \\ERROR: incomplete target '\foo.o' at position 0 | ||
| 811 | ); | ||
| 812 | try depTokenizer("$$foo.o", | ||
| 813 | \\ERROR: incomplete target '$foo.o' at position 0 | ||
| 814 | ); | ||
| 815 | } | ||
| 816 | |||
| 817 | test "error illegal char at position - bad target escape" { | ||
| 818 | try depTokenizer("\\\t", | ||
| 819 | \\ERROR: illegal char \x09 at position 1: bad target escape | ||
| 820 | ); | ||
| 821 | try depTokenizer("\\\n", | ||
| 822 | \\ERROR: illegal char \x0A at position 1: bad target escape | ||
| 823 | ); | ||
| 824 | try depTokenizer("\\\r", | ||
| 825 | \\ERROR: illegal char \x0D at position 1: bad target escape | ||
| 826 | ); | ||
| 827 | try depTokenizer("\\\r\n", | ||
| 828 | \\ERROR: illegal char \x0D at position 1: bad target escape | ||
| 829 | ); | ||
| 830 | } | ||
| 831 | |||
| 832 | test "error illegal char at position - execting dollar_sign" { | ||
| 833 | try depTokenizer("$\t", | ||
| 834 | \\ERROR: illegal char \x09 at position 1: expecting '$' | ||
| 835 | ); | ||
| 836 | try depTokenizer("$\n", | ||
| 837 | \\ERROR: illegal char \x0A at position 1: expecting '$' | ||
| 838 | ); | ||
| 839 | try depTokenizer("$\r", | ||
| 840 | \\ERROR: illegal char \x0D at position 1: expecting '$' | ||
| 841 | ); | ||
| 842 | try depTokenizer("$\r\n", | ||
| 843 | \\ERROR: illegal char \x0D at position 1: expecting '$' | ||
| 844 | ); | ||
| 845 | } | ||
| 846 | |||
| 847 | test "error illegal char at position - invalid target" { | ||
| 848 | try depTokenizer("foo\t.o", | ||
| 849 | \\ERROR: illegal char \x09 at position 3: invalid target | ||
| 850 | ); | ||
| 851 | try depTokenizer("foo\n.o", | ||
| 852 | \\ERROR: illegal char \x0A at position 3: invalid target | ||
| 853 | ); | ||
| 854 | try depTokenizer("foo\r.o", | ||
| 855 | \\ERROR: illegal char \x0D at position 3: invalid target | ||
| 856 | ); | ||
| 857 | try depTokenizer("foo\r\n.o", | ||
| 858 | \\ERROR: illegal char \x0D at position 3: invalid target | ||
| 859 | ); | ||
| 860 | } | ||
| 861 | |||
| 862 | test "error target - continuation expecting end-of-line" { | ||
| 863 | try depTokenizer("foo.o: \\\t", | ||
| 864 | \\target = {foo.o} | ||
| 865 | \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line | ||
| 866 | ); | ||
| 867 | try depTokenizer("foo.o: \\ ", | ||
| 868 | \\target = {foo.o} | ||
| 869 | \\ERROR: illegal char ' ' at position 8: continuation expecting end-of-line | ||
| 870 | ); | ||
| 871 | try depTokenizer("foo.o: \\x", | ||
| 872 | \\target = {foo.o} | ||
| 873 | \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line | ||
| 874 | ); | ||
| 875 | try depTokenizer("foo.o: \\\x0dx", | ||
| 876 | \\target = {foo.o} | ||
| 877 | \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line | ||
| 878 | ); | ||
| 879 | } | ||
| 880 | |||
| 881 | test "error prereq - continuation expecting end-of-line" { | ||
| 882 | try depTokenizer("foo.o: foo.h\\\x0dx", | ||
| 883 | \\target = {foo.o} | ||
| 884 | \\ERROR: illegal char 'x' at position 14: continuation expecting end-of-line | ||
| 885 | ); | ||
| 886 | } | ||
| 887 | |||
| 888 | // - tokenize input, emit textual representation, and compare to expect | ||
| 889 | fn depTokenizer(input: []const u8, expect: []const u8) !void { | ||
| 890 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); | ||
| 891 | const arena = arena_allocator.allocator(); | ||
| 892 | defer arena_allocator.deinit(); | ||
| 893 | |||
| 894 | var it: Tokenizer = .{ .bytes = input }; | ||
| 895 | var buffer = std.ArrayList(u8).init(arena); | ||
| 896 | var resolve_buf = std.ArrayList(u8).init(arena); | ||
| 897 | var i: usize = 0; | ||
| 898 | while (it.next()) |token| { | ||
| 899 | if (i != 0) try buffer.appendSlice("\n"); | ||
| 900 | switch (token) { | ||
| 901 | .target, .prereq => |bytes| { | ||
| 902 | try buffer.appendSlice(@tagName(token)); | ||
| 903 | try buffer.appendSlice(" = {"); | ||
| 904 | for (bytes) |b| { | ||
| 905 | try buffer.append(printable_char_tab[b]); | ||
| 906 | } | ||
| 907 | try buffer.appendSlice("}"); | ||
| 908 | }, | ||
| 909 | .target_must_resolve => { | ||
| 910 | try buffer.appendSlice("target = {"); | ||
| 911 | try token.resolve(resolve_buf.writer()); | ||
| 912 | for (resolve_buf.items) |b| { | ||
| 913 | try buffer.append(printable_char_tab[b]); | ||
| 914 | } | ||
| 915 | resolve_buf.items.len = 0; | ||
| 916 | try buffer.appendSlice("}"); | ||
| 917 | }, | ||
| 918 | else => { | ||
| 919 | try buffer.appendSlice("ERROR: "); | ||
| 920 | try token.printError(buffer.writer()); | ||
| 921 | break; | ||
| 922 | }, | ||
| 923 | } | ||
| 924 | i += 1; | ||
| 925 | } | ||
| 926 | |||
| 927 | if (std.mem.eql(u8, expect, buffer.items)) { | ||
| 928 | try testing.expect(true); | ||
| 929 | return; | ||
| 930 | } | ||
| 931 | |||
| 932 | const out = std.io.getStdErr().writer(); | ||
| 933 | |||
| 934 | try out.writeAll("\n"); | ||
| 935 | try printSection(out, "<<<< input", input); | ||
| 936 | try printSection(out, "==== expect", expect); | ||
| 937 | try printSection(out, ">>>> got", buffer.items); | ||
| 938 | try printRuler(out); | ||
| 939 | |||
| 940 | try testing.expect(false); | ||
| 941 | } | ||
| 942 | |||
| 943 | fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void { | ||
| 944 | try printLabel(out, label, bytes); | ||
| 945 | try hexDump(out, bytes); | ||
| 946 | try printRuler(out); | ||
| 947 | try out.writeAll(bytes); | ||
| 948 | try out.writeAll("\n"); | ||
| 949 | } | ||
| 950 | |||
| 951 | fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void { | ||
| 952 | var buf: [80]u8 = undefined; | ||
| 953 | var text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len }); | ||
| 954 | try out.writeAll(text); | ||
| 955 | var i: usize = text.len; | ||
| 956 | const end = 79; | ||
| 957 | while (i < end) : (i += 1) { | ||
| 958 | try out.writeAll(&[_]u8{label[0]}); | ||
| 959 | } | ||
| 960 | try out.writeAll("\n"); | ||
| 961 | } | ||
| 962 | |||
| 963 | fn printRuler(out: anytype) !void { | ||
| 964 | var i: usize = 0; | ||
| 965 | const end = 79; | ||
| 966 | while (i < end) : (i += 1) { | ||
| 967 | try out.writeAll("-"); | ||
| 968 | } | ||
| 969 | try out.writeAll("\n"); | ||
| 970 | } | ||
| 971 | |||
| 972 | fn hexDump(out: anytype, bytes: []const u8) !void { | ||
| 973 | const n16 = bytes.len >> 4; | ||
| 974 | var line: usize = 0; | ||
| 975 | var offset: usize = 0; | ||
| 976 | while (line < n16) : (line += 1) { | ||
| 977 | try hexDump16(out, offset, bytes[offset .. offset + 16]); | ||
| 978 | offset += 16; | ||
| 979 | } | ||
| 980 | |||
| 981 | const n = bytes.len & 0x0f; | ||
| 982 | if (n > 0) { | ||
| 983 | try printDecValue(out, offset, 8); | ||
| 984 | try out.writeAll(":"); | ||
| 985 | try out.writeAll(" "); | ||
| 986 | var end1 = std.math.min(offset + n, offset + 8); | ||
| 987 | for (bytes[offset..end1]) |b| { | ||
| 988 | try out.writeAll(" "); | ||
| 989 | try printHexValue(out, b, 2); | ||
| 990 | } | ||
| 991 | var end2 = offset + n; | ||
| 992 | if (end2 > end1) { | ||
| 993 | try out.writeAll(" "); | ||
| 994 | for (bytes[end1..end2]) |b| { | ||
| 995 | try out.writeAll(" "); | ||
| 996 | try printHexValue(out, b, 2); | ||
| 997 | } | ||
| 998 | } | ||
| 999 | const short = 16 - n; | ||
| 1000 | var i: usize = 0; | ||
| 1001 | while (i < short) : (i += 1) { | ||
| 1002 | try out.writeAll(" "); | ||
| 1003 | } | ||
| 1004 | if (end2 > end1) { | ||
| 1005 | try out.writeAll(" |"); | ||
| 1006 | } else { | ||
| 1007 | try out.writeAll(" |"); | ||
| 1008 | } | ||
| 1009 | try printCharValues(out, bytes[offset..end2]); | ||
| 1010 | try out.writeAll("|\n"); | ||
| 1011 | offset += n; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | try printDecValue(out, offset, 8); | ||
| 1015 | try out.writeAll(":"); | ||
| 1016 | try out.writeAll("\n"); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void { | ||
| 1020 | try printDecValue(out, offset, 8); | ||
| 1021 | try out.writeAll(":"); | ||
| 1022 | try out.writeAll(" "); | ||
| 1023 | for (bytes[0..8]) |b| { | ||
| 1024 | try out.writeAll(" "); | ||
| 1025 | try printHexValue(out, b, 2); | ||
| 1026 | } | ||
| 1027 | try out.writeAll(" "); | ||
| 1028 | for (bytes[8..16]) |b| { | ||
| 1029 | try out.writeAll(" "); | ||
| 1030 | try printHexValue(out, b, 2); | ||
| 1031 | } | ||
| 1032 | try out.writeAll(" |"); | ||
| 1033 | try printCharValues(out, bytes); | ||
| 1034 | try out.writeAll("|\n"); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | fn printDecValue(out: anytype, value: u64, width: u8) !void { | ||
| 1038 | var buffer: [20]u8 = undefined; | ||
| 1039 | const len = std.fmt.formatIntBuf(buffer[0..], value, 10, .lower, .{ .width = width, .fill = '0' }); | ||
| 1040 | try out.writeAll(buffer[0..len]); | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | fn printHexValue(out: anytype, value: u64, width: u8) !void { | ||
| 1044 | var buffer: [16]u8 = undefined; | ||
| 1045 | const len = std.fmt.formatIntBuf(buffer[0..], value, 16, .lower, .{ .width = width, .fill = '0' }); | ||
| 1046 | try out.writeAll(buffer[0..len]); | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | fn printCharValues(out: anytype, bytes: []const u8) !void { | ||
| 1050 | for (bytes) |b| { | ||
| 1051 | try out.writeAll(&[_]u8{printable_char_tab[b]}); | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | fn printUnderstandableChar(out: anytype, char: u8) !void { | ||
| 1056 | if (std.ascii.isPrint(char)) { | ||
| 1057 | try out.print("'{c}'", .{char}); | ||
| 1058 | } else { | ||
| 1059 | try out.print("\\x{X:0>2}", .{char}); | ||
| 1060 | } | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | // zig fmt: off | ||
| 1064 | const printable_char_tab: [256]u8 = ( | ||
| 1065 | "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++ | ||
| 1066 | "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++ | ||
| 1067 | "................................................................" ++ | ||
| 1068 | "................................................................" | ||
| 1069 | ).*; | ||
lib/std/Build/CompileStep.zig+46-43| ... | @@ -83,7 +83,7 @@ max_memory: ?u64 = null, | ... | @@ -83,7 +83,7 @@ max_memory: ?u64 = null, |
| 83 | shared_memory: bool = false, | 83 | shared_memory: bool = false, |
| 84 | global_base: ?u64 = null, | 84 | global_base: ?u64 = null, |
| 85 | c_std: std.Build.CStd, | 85 | c_std: std.Build.CStd, |
| 86 | override_lib_dir: ?[]const u8, | 86 | zig_lib_dir: ?[]const u8, |
| 87 | main_pkg_path: ?[]const u8, | 87 | main_pkg_path: ?[]const u8, |
| 88 | exec_cmd_args: ?[]const ?[]const u8, | 88 | exec_cmd_args: ?[]const ?[]const u8, |
| 89 | name_prefix: []const u8, | 89 | name_prefix: []const u8, |
| ... | @@ -344,7 +344,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { | ... | @@ -344,7 +344,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { |
| 344 | .installed_headers = ArrayList(*Step).init(builder.allocator), | 344 | .installed_headers = ArrayList(*Step).init(builder.allocator), |
| 345 | .object_src = undefined, | 345 | .object_src = undefined, |
| 346 | .c_std = std.Build.CStd.C99, | 346 | .c_std = std.Build.CStd.C99, |
| 347 | .override_lib_dir = null, | 347 | .zig_lib_dir = null, |
| 348 | .main_pkg_path = null, | 348 | .main_pkg_path = null, |
| 349 | .exec_cmd_args = null, | 349 | .exec_cmd_args = null, |
| 350 | .name_prefix = "", | 350 | .name_prefix = "", |
| ... | @@ -442,6 +442,26 @@ pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []con | ... | @@ -442,6 +442,26 @@ pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []con |
| 442 | a.installed_headers.append(&install_file.step) catch @panic("OOM"); | 442 | a.installed_headers.append(&install_file.step) catch @panic("OOM"); |
| 443 | } | 443 | } |
| 444 | 444 | ||
| 445 | pub const InstallConfigHeaderOptions = struct { | ||
| 446 | install_dir: InstallDir = .header, | ||
| 447 | dest_rel_path: ?[]const u8 = null, | ||
| 448 | }; | ||
| 449 | |||
| 450 | pub fn installConfigHeader( | ||
| 451 | cs: *CompileStep, | ||
| 452 | config_header: *ConfigHeaderStep, | ||
| 453 | options: InstallConfigHeaderOptions, | ||
| 454 | ) void { | ||
| 455 | const dest_rel_path = options.dest_rel_path orelse config_header.include_path; | ||
| 456 | const install_file = cs.builder.addInstallFileWithDir( | ||
| 457 | .{ .generated = &config_header.output_file }, | ||
| 458 | options.install_dir, | ||
| 459 | dest_rel_path, | ||
| 460 | ); | ||
| 461 | cs.builder.getInstallStep().dependOn(&install_file.step); | ||
| 462 | cs.installed_headers.append(&install_file.step) catch @panic("OOM"); | ||
| 463 | } | ||
| 464 | |||
| 445 | pub fn installHeadersDirectory( | 465 | pub fn installHeadersDirectory( |
| 446 | a: *CompileStep, | 466 | a: *CompileStep, |
| 447 | src_dir_path: []const u8, | 467 | src_dir_path: []const u8, |
| ... | @@ -486,26 +506,11 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { | ... | @@ -486,26 +506,11 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { |
| 486 | a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); | 506 | a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); |
| 487 | } | 507 | } |
| 488 | 508 | ||
| 489 | /// Creates a `RunStep` with an executable built with `addExecutable`. | 509 | /// Deprecated: use `std.Build.addRunArtifact` |
| 490 | /// Add command line arguments with `addArg`. | 510 | /// This function will run in the context of the package that created the executable, |
| 511 | /// which is undesirable when running an executable provided by a dependency package. | ||
| 491 | pub fn run(exe: *CompileStep) *RunStep { | 512 | pub fn run(exe: *CompileStep) *RunStep { |
| 492 | assert(exe.kind == .exe or exe.kind == .test_exe); | 513 | return exe.builder.addRunArtifact(exe); |
| 493 | |||
| 494 | // It doesn't have to be native. We catch that if you actually try to run it. | ||
| 495 | // Consider that this is declarative; the run step may not be run unless a user | ||
| 496 | // option is supplied. | ||
| 497 | const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); | ||
| 498 | run_step.addArtifactArg(exe); | ||
| 499 | |||
| 500 | if (exe.kind == .test_exe) { | ||
| 501 | run_step.addArg(exe.builder.zig_exe); | ||
| 502 | } | ||
| 503 | |||
| 504 | if (exe.vcpkg_bin_path) |path| { | ||
| 505 | run_step.addPathDir(path); | ||
| 506 | } | ||
| 507 | |||
| 508 | return run_step; | ||
| 509 | } | 514 | } |
| 510 | 515 | ||
| 511 | /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. | 516 | /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. |
| ... | @@ -852,7 +857,7 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void { | ... | @@ -852,7 +857,7 @@ pub fn setVerboseCC(self: *CompileStep, value: bool) void { |
| 852 | } | 857 | } |
| 853 | 858 | ||
| 854 | pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { | 859 | pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { |
| 855 | self.override_lib_dir = self.builder.dupePath(dir_path); | 860 | self.zig_lib_dir = self.builder.dupePath(dir_path); |
| 856 | } | 861 | } |
| 857 | 862 | ||
| 858 | pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { | 863 | pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { |
| ... | @@ -1345,10 +1350,10 @@ fn make(step: *Step) !void { | ... | @@ -1345,10 +1350,10 @@ fn make(step: *Step) !void { |
| 1345 | } | 1350 | } |
| 1346 | 1351 | ||
| 1347 | try zig_args.append("--cache-dir"); | 1352 | try zig_args.append("--cache-dir"); |
| 1348 | try zig_args.append(builder.pathFromRoot(builder.cache_root)); | 1353 | try zig_args.append(builder.cache_root.path orelse "."); |
| 1349 | 1354 | ||
| 1350 | try zig_args.append("--global-cache-dir"); | 1355 | try zig_args.append("--global-cache-dir"); |
| 1351 | try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); | 1356 | try zig_args.append(builder.global_cache_root.path orelse "."); |
| 1352 | 1357 | ||
| 1353 | try zig_args.append("--name"); | 1358 | try zig_args.append("--name"); |
| 1354 | try zig_args.append(self.name); | 1359 | try zig_args.append(self.name); |
| ... | @@ -1622,8 +1627,9 @@ fn make(step: *Step) !void { | ... | @@ -1622,8 +1627,9 @@ fn make(step: *Step) !void { |
| 1622 | } | 1627 | } |
| 1623 | }, | 1628 | }, |
| 1624 | .config_header_step => |config_header| { | 1629 | .config_header_step => |config_header| { |
| 1625 | try zig_args.append("-I"); | 1630 | const full_file_path = config_header.output_file.path.?; |
| 1626 | try zig_args.append(config_header.output_dir); | 1631 | const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len]; |
| 1632 | try zig_args.appendSlice(&.{ "-I", header_dir_path }); | ||
| 1627 | }, | 1633 | }, |
| 1628 | } | 1634 | } |
| 1629 | } | 1635 | } |
| ... | @@ -1697,12 +1703,12 @@ fn make(step: *Step) !void { | ... | @@ -1697,12 +1703,12 @@ fn make(step: *Step) !void { |
| 1697 | try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); | 1703 | try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); |
| 1698 | try addFlag(&zig_args, "build-id", self.build_id); | 1704 | try addFlag(&zig_args, "build-id", self.build_id); |
| 1699 | 1705 | ||
| 1700 | if (self.override_lib_dir) |dir| { | 1706 | if (self.zig_lib_dir) |dir| { |
| 1701 | try zig_args.append("--zig-lib-dir"); | 1707 | try zig_args.append("--zig-lib-dir"); |
| 1702 | try zig_args.append(builder.pathFromRoot(dir)); | 1708 | try zig_args.append(builder.pathFromRoot(dir)); |
| 1703 | } else if (builder.override_lib_dir) |dir| { | 1709 | } else if (builder.zig_lib_dir) |dir| { |
| 1704 | try zig_args.append("--zig-lib-dir"); | 1710 | try zig_args.append("--zig-lib-dir"); |
| 1705 | try zig_args.append(builder.pathFromRoot(dir)); | 1711 | try zig_args.append(dir); |
| 1706 | } | 1712 | } |
| 1707 | 1713 | ||
| 1708 | if (self.main_pkg_path) |dir| { | 1714 | if (self.main_pkg_path) |dir| { |
| ... | @@ -1739,23 +1745,15 @@ fn make(step: *Step) !void { | ... | @@ -1739,23 +1745,15 @@ fn make(step: *Step) !void { |
| 1739 | args_length += arg.len + 1; // +1 to account for null terminator | 1745 | args_length += arg.len + 1; // +1 to account for null terminator |
| 1740 | } | 1746 | } |
| 1741 | if (args_length >= 30 * 1024) { | 1747 | if (args_length >= 30 * 1024) { |
| 1742 | const args_dir = try fs.path.join( | 1748 | try builder.cache_root.handle.makePath("args"); |
| 1743 | builder.allocator, | ||
| 1744 | &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" }, | ||
| 1745 | ); | ||
| 1746 | try std.fs.cwd().makePath(args_dir); | ||
| 1747 | |||
| 1748 | var args_arena = std.heap.ArenaAllocator.init(builder.allocator); | ||
| 1749 | defer args_arena.deinit(); | ||
| 1750 | 1749 | ||
| 1751 | const args_to_escape = zig_args.items[2..]; | 1750 | const args_to_escape = zig_args.items[2..]; |
| 1752 | var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len); | 1751 | var escaped_args = try ArrayList([]const u8).initCapacity(builder.allocator, args_to_escape.len); |
| 1753 | |||
| 1754 | arg_blk: for (args_to_escape) |arg| { | 1752 | arg_blk: for (args_to_escape) |arg| { |
| 1755 | for (arg) |c, arg_idx| { | 1753 | for (arg) |c, arg_idx| { |
| 1756 | if (c == '\\' or c == '"') { | 1754 | if (c == '\\' or c == '"') { |
| 1757 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy | 1755 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy |
| 1758 | var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); | 1756 | var escaped = try ArrayList(u8).initCapacity(builder.allocator, arg.len + 1); |
| 1759 | const writer = escaped.writer(); | 1757 | const writer = escaped.writer(); |
| 1760 | try writer.writeAll(arg[0..arg_idx]); | 1758 | try writer.writeAll(arg[0..arg_idx]); |
| 1761 | for (arg[arg_idx..]) |to_escape| { | 1759 | for (arg[arg_idx..]) |to_escape| { |
| ... | @@ -1783,11 +1781,16 @@ fn make(step: *Step) !void { | ... | @@ -1783,11 +1781,16 @@ fn make(step: *Step) !void { |
| 1783 | .{std.fmt.fmtSliceHexLower(&args_hash)}, | 1781 | .{std.fmt.fmtSliceHexLower(&args_hash)}, |
| 1784 | ); | 1782 | ); |
| 1785 | 1783 | ||
| 1786 | const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] }); | 1784 | const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; |
| 1787 | try std.fs.cwd().writeFile(args_file, args); | 1785 | try builder.cache_root.handle.writeFile(args_file, args); |
| 1786 | |||
| 1787 | const resolved_args_file = try mem.concat(builder.allocator, u8, &.{ | ||
| 1788 | "@", | ||
| 1789 | try builder.cache_root.join(builder.allocator, &.{args_file}), | ||
| 1790 | }); | ||
| 1788 | 1791 | ||
| 1789 | zig_args.shrinkRetainingCapacity(2); | 1792 | zig_args.shrinkRetainingCapacity(2); |
| 1790 | try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file })); | 1793 | try zig_args.append(resolved_args_file); |
| 1791 | } | 1794 | } |
| 1792 | 1795 | ||
| 1793 | const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); | 1796 | const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); |
lib/std/Build/ConfigHeaderStep.zig+189-60| ... | @@ -4,13 +4,24 @@ const Step = std.Build.Step; | ... | @@ -4,13 +4,24 @@ const Step = std.Build.Step; |
| 4 | 4 | ||
| 5 | pub const base_id: Step.Id = .config_header; | 5 | pub const base_id: Step.Id = .config_header; |
| 6 | 6 | ||
| 7 | pub const Style = enum { | 7 | pub const Style = union(enum) { |
| 8 | /// The configure format supported by autotools. It uses `#undef foo` to | 8 | /// The configure format supported by autotools. It uses `#undef foo` to |
| 9 | /// mark lines that can be substituted with different values. | 9 | /// mark lines that can be substituted with different values. |
| 10 | autoconf, | 10 | autoconf: std.Build.FileSource, |
| 11 | /// The configure format supported by CMake. It uses `@@FOO@@` and | 11 | /// The configure format supported by CMake. It uses `@@FOO@@` and |
| 12 | /// `#cmakedefine` for template substitution. | 12 | /// `#cmakedefine` for template substitution. |
| 13 | cmake, | 13 | cmake: std.Build.FileSource, |
| 14 | /// Instead of starting with an input file, start with nothing. | ||
| 15 | blank, | ||
| 16 | /// Start with nothing, like blank, and output a nasm .asm file. | ||
| 17 | nasm, | ||
| 18 | |||
| 19 | pub fn getFileSource(style: Style) ?std.Build.FileSource { | ||
| 20 | switch (style) { | ||
| 21 | .autoconf, .cmake => |s| return s, | ||
| 22 | .blank, .nasm => return null, | ||
| 23 | } | ||
| 24 | } | ||
| 14 | }; | 25 | }; |
| 15 | 26 | ||
| 16 | pub const Value = union(enum) { | 27 | pub const Value = union(enum) { |
| ... | @@ -24,34 +35,50 @@ pub const Value = union(enum) { | ... | @@ -24,34 +35,50 @@ pub const Value = union(enum) { |
| 24 | 35 | ||
| 25 | step: Step, | 36 | step: Step, |
| 26 | builder: *std.Build, | 37 | builder: *std.Build, |
| 27 | source: std.Build.FileSource, | 38 | values: std.StringArrayHashMap(Value), |
| 39 | output_file: std.Build.GeneratedFile, | ||
| 40 | |||
| 28 | style: Style, | 41 | style: Style, |
| 29 | values: std.StringHashMap(Value), | 42 | max_bytes: usize, |
| 30 | max_bytes: usize = 2 * 1024 * 1024, | 43 | include_path: []const u8, |
| 31 | output_dir: []const u8, | 44 | |
| 32 | output_basename: []const u8, | 45 | pub const Options = struct { |
| 46 | style: Style = .blank, | ||
| 47 | max_bytes: usize = 2 * 1024 * 1024, | ||
| 48 | include_path: ?[]const u8 = null, | ||
| 49 | }; | ||
| 33 | 50 | ||
| 34 | pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep { | 51 | pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep { |
| 35 | const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); | 52 | const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); |
| 36 | const name = builder.fmt("configure header {s}", .{source.getDisplayName()}); | 53 | const name = if (options.style.getFileSource()) |s| |
| 54 | builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() }) | ||
| 55 | else | ||
| 56 | builder.fmt("configure {s} header", .{@tagName(options.style)}); | ||
| 37 | self.* = .{ | 57 | self.* = .{ |
| 38 | .builder = builder, | 58 | .builder = builder, |
| 39 | .step = Step.init(base_id, name, builder.allocator, make), | 59 | .step = Step.init(base_id, name, builder.allocator, make), |
| 40 | .source = source, | 60 | .style = options.style, |
| 41 | .style = style, | 61 | .values = std.StringArrayHashMap(Value).init(builder.allocator), |
| 42 | .values = std.StringHashMap(Value).init(builder.allocator), | 62 | |
| 43 | .output_dir = undefined, | 63 | .max_bytes = options.max_bytes, |
| 44 | .output_basename = "config.h", | 64 | .include_path = "config.h", |
| 65 | .output_file = .{ .step = &self.step }, | ||
| 45 | }; | 66 | }; |
| 46 | switch (source) { | 67 | |
| 68 | if (options.style.getFileSource()) |s| switch (s) { | ||
| 47 | .path => |p| { | 69 | .path => |p| { |
| 48 | const basename = std.fs.path.basename(p); | 70 | const basename = std.fs.path.basename(p); |
| 49 | if (std.mem.endsWith(u8, basename, ".h.in")) { | 71 | if (std.mem.endsWith(u8, basename, ".h.in")) { |
| 50 | self.output_basename = basename[0 .. basename.len - 3]; | 72 | self.include_path = basename[0 .. basename.len - 3]; |
| 51 | } | 73 | } |
| 52 | }, | 74 | }, |
| 53 | else => {}, | 75 | else => {}, |
| 76 | }; | ||
| 77 | |||
| 78 | if (options.include_path) |include_path| { | ||
| 79 | self.include_path = include_path; | ||
| 54 | } | 80 | } |
| 81 | |||
| 55 | return self; | 82 | return self; |
| 56 | } | 83 | } |
| 57 | 84 | ||
| ... | @@ -59,6 +86,10 @@ pub fn addValues(self: *ConfigHeaderStep, values: anytype) void { | ... | @@ -59,6 +86,10 @@ pub fn addValues(self: *ConfigHeaderStep, values: anytype) void { |
| 59 | return addValuesInner(self, values) catch @panic("OOM"); | 86 | return addValuesInner(self, values) catch @panic("OOM"); |
| 60 | } | 87 | } |
| 61 | 88 | ||
| 89 | pub fn getFileSource(self: *ConfigHeaderStep) std.Build.FileSource { | ||
| 90 | return .{ .generated = &self.output_file }; | ||
| 91 | } | ||
| 92 | |||
| 62 | fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void { | 93 | fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void { |
| 63 | inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| { | 94 | inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| { |
| 64 | try putValue(self, field.name, field.type, @field(values, field.name)); | 95 | try putValue(self, field.name, field.type, @field(values, field.name)); |
| ... | @@ -100,6 +131,12 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v | ... | @@ -100,6 +131,12 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v |
| 100 | return; | 131 | return; |
| 101 | } | 132 | } |
| 102 | }, | 133 | }, |
| 134 | .Int => { | ||
| 135 | if (ptr.size == .Slice and ptr.child == u8) { | ||
| 136 | try self.values.put(field_name, .{ .string = v }); | ||
| 137 | return; | ||
| 138 | } | ||
| 139 | }, | ||
| 103 | else => {}, | 140 | else => {}, |
| 104 | } | 141 | } |
| 105 | 142 | ||
| ... | @@ -112,8 +149,6 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v | ... | @@ -112,8 +149,6 @@ fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v |
| 112 | fn make(step: *Step) !void { | 149 | fn make(step: *Step) !void { |
| 113 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); | 150 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); |
| 114 | const gpa = self.builder.allocator; | 151 | const gpa = self.builder.allocator; |
| 115 | const src_path = self.source.getPath(self.builder); | ||
| 116 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | ||
| 117 | 152 | ||
| 118 | // The cache is used here not really as a way to speed things up - because writing | 153 | // The cache is used here not really as a way to speed things up - because writing |
| 119 | // the data to a file would probably be very fast - but as a way to find a canonical | 154 | // the data to a file would probably be very fast - but as a way to find a canonical |
| ... | @@ -130,9 +165,39 @@ fn make(step: *Step) !void { | ... | @@ -130,9 +165,39 @@ fn make(step: *Step) !void { |
| 130 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new | 165 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new |
| 131 | // random bytes when ConfigHeaderStep implementation is modified in a | 166 | // random bytes when ConfigHeaderStep implementation is modified in a |
| 132 | // non-backwards-compatible way. | 167 | // non-backwards-compatible way. |
| 133 | var hash = Hasher.init("X1pQzdDt91Zlh7Eh"); | 168 | var hash = Hasher.init("PGuDTpidxyMqnkGM"); |
| 134 | hash.update(self.source.getDisplayName()); | 169 | |
| 135 | hash.update(contents); | 170 | var output = std.ArrayList(u8).init(gpa); |
| 171 | defer output.deinit(); | ||
| 172 | |||
| 173 | const header_text = "This file was generated by ConfigHeaderStep using the Zig Build System."; | ||
| 174 | const c_generated_line = "/* " ++ header_text ++ " */\n"; | ||
| 175 | const asm_generated_line = "; " ++ header_text ++ "\n"; | ||
| 176 | |||
| 177 | switch (self.style) { | ||
| 178 | .autoconf => |file_source| { | ||
| 179 | try output.appendSlice(c_generated_line); | ||
| 180 | const src_path = file_source.getPath(self.builder); | ||
| 181 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | ||
| 182 | try render_autoconf(contents, &output, self.values, src_path); | ||
| 183 | }, | ||
| 184 | .cmake => |file_source| { | ||
| 185 | try output.appendSlice(c_generated_line); | ||
| 186 | const src_path = file_source.getPath(self.builder); | ||
| 187 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | ||
| 188 | try render_cmake(contents, &output, self.values, src_path); | ||
| 189 | }, | ||
| 190 | .blank => { | ||
| 191 | try output.appendSlice(c_generated_line); | ||
| 192 | try render_blank(&output, self.values, self.include_path); | ||
| 193 | }, | ||
| 194 | .nasm => { | ||
| 195 | try output.appendSlice(asm_generated_line); | ||
| 196 | try render_nasm(&output, self.values); | ||
| 197 | }, | ||
| 198 | } | ||
| 199 | |||
| 200 | hash.update(output.items); | ||
| 136 | 201 | ||
| 137 | var digest: [16]u8 = undefined; | 202 | var digest: [16]u8 = undefined; |
| 138 | hash.final(&digest); | 203 | hash.final(&digest); |
| ... | @@ -143,38 +208,40 @@ fn make(step: *Step) !void { | ... | @@ -143,38 +208,40 @@ fn make(step: *Step) !void { |
| 143 | .{std.fmt.fmtSliceHexLower(&digest)}, | 208 | .{std.fmt.fmtSliceHexLower(&digest)}, |
| 144 | ) catch unreachable; | 209 | ) catch unreachable; |
| 145 | 210 | ||
| 146 | self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{ | 211 | const output_dir = try self.builder.cache_root.join(gpa, &.{ "o", &hash_basename }); |
| 147 | self.builder.cache_root, "o", &hash_basename, | 212 | |
| 148 | }); | 213 | // If output_path has directory parts, deal with them. Example: |
| 149 | var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| { | 214 | // output_dir is zig-cache/o/HASH |
| 150 | std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | 215 | // output_path is libavutil/avconfig.h |
| 216 | // We want to open directory zig-cache/o/HASH/libavutil/ | ||
| 217 | // but keep output_dir as zig-cache/o/HASH for -I include | ||
| 218 | const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d| | ||
| 219 | try std.fs.path.join(gpa, &.{ output_dir, d }) | ||
| 220 | else | ||
| 221 | output_dir; | ||
| 222 | |||
| 223 | var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| { | ||
| 224 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | ||
| 151 | return err; | 225 | return err; |
| 152 | }; | 226 | }; |
| 153 | defer dir.close(); | 227 | defer dir.close(); |
| 154 | 228 | ||
| 155 | var values_copy = try self.values.clone(); | 229 | try dir.writeFile(std.fs.path.basename(self.include_path), output.items); |
| 156 | defer values_copy.deinit(); | ||
| 157 | |||
| 158 | var output = std.ArrayList(u8).init(gpa); | ||
| 159 | defer output.deinit(); | ||
| 160 | try output.ensureTotalCapacity(contents.len); | ||
| 161 | |||
| 162 | try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n"); | ||
| 163 | 230 | ||
| 164 | switch (self.style) { | 231 | self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{ |
| 165 | .autoconf => try render_autoconf(contents, &output, &values_copy, src_path), | 232 | output_dir, self.include_path, |
| 166 | .cmake => try render_cmake(contents, &output, &values_copy, src_path), | 233 | }); |
| 167 | } | ||
| 168 | |||
| 169 | try dir.writeFile(self.output_basename, output.items); | ||
| 170 | } | 234 | } |
| 171 | 235 | ||
| 172 | fn render_autoconf( | 236 | fn render_autoconf( |
| 173 | contents: []const u8, | 237 | contents: []const u8, |
| 174 | output: *std.ArrayList(u8), | 238 | output: *std.ArrayList(u8), |
| 175 | values_copy: *std.StringHashMap(Value), | 239 | values: std.StringArrayHashMap(Value), |
| 176 | src_path: []const u8, | 240 | src_path: []const u8, |
| 177 | ) !void { | 241 | ) !void { |
| 242 | var values_copy = try values.clone(); | ||
| 243 | defer values_copy.deinit(); | ||
| 244 | |||
| 178 | var any_errors = false; | 245 | var any_errors = false; |
| 179 | var line_index: u32 = 0; | 246 | var line_index: u32 = 0; |
| 180 | var line_it = std.mem.split(u8, contents, "\n"); | 247 | var line_it = std.mem.split(u8, contents, "\n"); |
| ... | @@ -192,22 +259,18 @@ fn render_autoconf( | ... | @@ -192,22 +259,18 @@ fn render_autoconf( |
| 192 | continue; | 259 | continue; |
| 193 | } | 260 | } |
| 194 | const name = it.rest(); | 261 | const name = it.rest(); |
| 195 | const kv = values_copy.fetchRemove(name) orelse { | 262 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 196 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 263 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ |
| 197 | src_path, line_index + 1, name, | 264 | src_path, line_index + 1, name, |
| 198 | }); | 265 | }); |
| 199 | any_errors = true; | 266 | any_errors = true; |
| 200 | continue; | 267 | continue; |
| 201 | }; | 268 | }; |
| 202 | try renderValue(output, name, kv.value); | 269 | try renderValueC(output, name, kv.value); |
| 203 | } | 270 | } |
| 204 | 271 | ||
| 205 | { | 272 | for (values_copy.keys()) |name| { |
| 206 | var it = values_copy.iterator(); | 273 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); |
| 207 | while (it.next()) |entry| { | ||
| 208 | const name = entry.key_ptr.*; | ||
| 209 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | ||
| 210 | } | ||
| 211 | } | 274 | } |
| 212 | 275 | ||
| 213 | if (any_errors) { | 276 | if (any_errors) { |
| ... | @@ -218,9 +281,12 @@ fn render_autoconf( | ... | @@ -218,9 +281,12 @@ fn render_autoconf( |
| 218 | fn render_cmake( | 281 | fn render_cmake( |
| 219 | contents: []const u8, | 282 | contents: []const u8, |
| 220 | output: *std.ArrayList(u8), | 283 | output: *std.ArrayList(u8), |
| 221 | values_copy: *std.StringHashMap(Value), | 284 | values: std.StringArrayHashMap(Value), |
| 222 | src_path: []const u8, | 285 | src_path: []const u8, |
| 223 | ) !void { | 286 | ) !void { |
| 287 | var values_copy = try values.clone(); | ||
| 288 | defer values_copy.deinit(); | ||
| 289 | |||
| 224 | var any_errors = false; | 290 | var any_errors = false; |
| 225 | var line_index: u32 = 0; | 291 | var line_index: u32 = 0; |
| 226 | var line_it = std.mem.split(u8, contents, "\n"); | 292 | var line_it = std.mem.split(u8, contents, "\n"); |
| ... | @@ -244,22 +310,18 @@ fn render_cmake( | ... | @@ -244,22 +310,18 @@ fn render_cmake( |
| 244 | any_errors = true; | 310 | any_errors = true; |
| 245 | continue; | 311 | continue; |
| 246 | }; | 312 | }; |
| 247 | const kv = values_copy.fetchRemove(name) orelse { | 313 | const kv = values_copy.fetchSwapRemove(name) orelse { |
| 248 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | 314 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ |
| 249 | src_path, line_index + 1, name, | 315 | src_path, line_index + 1, name, |
| 250 | }); | 316 | }); |
| 251 | any_errors = true; | 317 | any_errors = true; |
| 252 | continue; | 318 | continue; |
| 253 | }; | 319 | }; |
| 254 | try renderValue(output, name, kv.value); | 320 | try renderValueC(output, name, kv.value); |
| 255 | } | 321 | } |
| 256 | 322 | ||
| 257 | { | 323 | for (values_copy.keys()) |name| { |
| 258 | var it = values_copy.iterator(); | 324 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); |
| 259 | while (it.next()) |entry| { | ||
| 260 | const name = entry.key_ptr.*; | ||
| 261 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | ||
| 262 | } | ||
| 263 | } | 325 | } |
| 264 | 326 | ||
| 265 | if (any_errors) { | 327 | if (any_errors) { |
| ... | @@ -267,7 +329,44 @@ fn render_cmake( | ... | @@ -267,7 +329,44 @@ fn render_cmake( |
| 267 | } | 329 | } |
| 268 | } | 330 | } |
| 269 | 331 | ||
| 270 | fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void { | 332 | fn render_blank( |
| 333 | output: *std.ArrayList(u8), | ||
| 334 | defines: std.StringArrayHashMap(Value), | ||
| 335 | include_path: []const u8, | ||
| 336 | ) !void { | ||
| 337 | const include_guard_name = try output.allocator.dupe(u8, include_path); | ||
| 338 | for (include_guard_name) |*byte| { | ||
| 339 | switch (byte.*) { | ||
| 340 | 'a'...'z' => byte.* = byte.* - 'a' + 'A', | ||
| 341 | 'A'...'Z', '0'...'9' => continue, | ||
| 342 | else => byte.* = '_', | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | try output.appendSlice("#ifndef "); | ||
| 347 | try output.appendSlice(include_guard_name); | ||
| 348 | try output.appendSlice("\n#define "); | ||
| 349 | try output.appendSlice(include_guard_name); | ||
| 350 | try output.appendSlice("\n"); | ||
| 351 | |||
| 352 | const values = defines.values(); | ||
| 353 | for (defines.keys()) |name, i| { | ||
| 354 | try renderValueC(output, name, values[i]); | ||
| 355 | } | ||
| 356 | |||
| 357 | try output.appendSlice("#endif /* "); | ||
| 358 | try output.appendSlice(include_guard_name); | ||
| 359 | try output.appendSlice(" */\n"); | ||
| 360 | } | ||
| 361 | |||
| 362 | fn render_nasm(output: *std.ArrayList(u8), defines: std.StringArrayHashMap(Value)) !void { | ||
| 363 | const values = defines.values(); | ||
| 364 | for (defines.keys()) |name, i| { | ||
| 365 | try renderValueNasm(output, name, values[i]); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | fn renderValueC(output: *std.ArrayList(u8), name: []const u8, value: Value) !void { | ||
| 271 | switch (value) { | 370 | switch (value) { |
| 272 | .undef => { | 371 | .undef => { |
| 273 | try output.appendSlice("/* #undef "); | 372 | try output.appendSlice("/* #undef "); |
| ... | @@ -297,3 +396,33 @@ fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void | ... | @@ -297,3 +396,33 @@ fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void |
| 297 | }, | 396 | }, |
| 298 | } | 397 | } |
| 299 | } | 398 | } |
| 399 | |||
| 400 | fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !void { | ||
| 401 | switch (value) { | ||
| 402 | .undef => { | ||
| 403 | try output.appendSlice("; %undef "); | ||
| 404 | try output.appendSlice(name); | ||
| 405 | try output.appendSlice("\n"); | ||
| 406 | }, | ||
| 407 | .defined => { | ||
| 408 | try output.appendSlice("%define "); | ||
| 409 | try output.appendSlice(name); | ||
| 410 | try output.appendSlice("\n"); | ||
| 411 | }, | ||
| 412 | .boolean => |b| { | ||
| 413 | try output.appendSlice("%define "); | ||
| 414 | try output.appendSlice(name); | ||
| 415 | try output.appendSlice(if (b) " 1\n" else " 0\n"); | ||
| 416 | }, | ||
| 417 | .int => |i| { | ||
| 418 | try output.writer().print("%define {s} {d}\n", .{ name, i }); | ||
| 419 | }, | ||
| 420 | .ident => |ident| { | ||
| 421 | try output.writer().print("%define {s} {s}\n", .{ name, ident }); | ||
| 422 | }, | ||
| 423 | .string => |string| { | ||
| 424 | // TODO: use nasm-specific escaping instead of zig string literals | ||
| 425 | try output.writer().print("%define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) }); | ||
| 426 | }, | ||
| 427 | } | ||
| 428 | } |
lib/std/Build/OptionsStep.zig+17-17| ... | @@ -234,26 +234,20 @@ fn make(step: *Step) !void { | ... | @@ -234,26 +234,20 @@ fn make(step: *Step) !void { |
| 234 | ); | 234 | ); |
| 235 | } | 235 | } |
| 236 | 236 | ||
| 237 | const options_directory = self.builder.pathFromRoot( | 237 | var options_dir = try self.builder.cache_root.handle.makeOpenPath("options", .{}); |
| 238 | try fs.path.join( | 238 | defer options_dir.close(); |
| 239 | self.builder.allocator, | ||
| 240 | &[_][]const u8{ self.builder.cache_root, "options" }, | ||
| 241 | ), | ||
| 242 | ); | ||
| 243 | |||
| 244 | try fs.cwd().makePath(options_directory); | ||
| 245 | 239 | ||
| 246 | const options_file = try fs.path.join( | 240 | const basename = self.hashContentsToFileName(); |
| 247 | self.builder.allocator, | ||
| 248 | &[_][]const u8{ options_directory, &self.hashContentsToFileName() }, | ||
| 249 | ); | ||
| 250 | 241 | ||
| 251 | try fs.cwd().writeFile(options_file, self.contents.items); | 242 | try options_dir.writeFile(&basename, self.contents.items); |
| 252 | 243 | ||
| 253 | self.generated_file.path = options_file; | 244 | self.generated_file.path = try self.builder.cache_root.join(self.builder.allocator, &.{ |
| 245 | "options", &basename, | ||
| 246 | }); | ||
| 254 | } | 247 | } |
| 255 | 248 | ||
| 256 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { | 249 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { |
| 250 | // TODO update to use the cache system instead of this | ||
| 257 | // This implementation is copied from `WriteFileStep.make` | 251 | // This implementation is copied from `WriteFileStep.make` |
| 258 | 252 | ||
| 259 | var hash = std.crypto.hash.blake2.Blake2b384.init(.{}); | 253 | var hash = std.crypto.hash.blake2.Blake2b384.init(.{}); |
| ... | @@ -289,13 +283,19 @@ test "OptionsStep" { | ... | @@ -289,13 +283,19 @@ test "OptionsStep" { |
| 289 | 283 | ||
| 290 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); | 284 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); |
| 291 | 285 | ||
| 286 | var cache: std.Build.Cache = .{ | ||
| 287 | .gpa = arena.allocator(), | ||
| 288 | .manifest_dir = std.fs.cwd(), | ||
| 289 | }; | ||
| 290 | |||
| 292 | var builder = try std.Build.create( | 291 | var builder = try std.Build.create( |
| 293 | arena.allocator(), | 292 | arena.allocator(), |
| 294 | "test", | 293 | "test", |
| 295 | "test", | 294 | .{ .path = "test", .handle = std.fs.cwd() }, |
| 296 | "test", | 295 | .{ .path = "test", .handle = std.fs.cwd() }, |
| 297 | "test", | 296 | .{ .path = "test", .handle = std.fs.cwd() }, |
| 298 | host, | 297 | host, |
| 298 | &cache, | ||
| 299 | ); | 299 | ); |
| 300 | defer builder.destroy(); | 300 | defer builder.destroy(); |
| 301 | 301 |
lib/std/Build/RunStep.zig+133-6| ... | @@ -39,6 +39,14 @@ expected_exit_code: ?u8 = 0, | ... | @@ -39,6 +39,14 @@ expected_exit_code: ?u8 = 0, |
| 39 | 39 | ||
| 40 | /// Print the command before running it | 40 | /// Print the command before running it |
| 41 | print: bool, | 41 | print: bool, |
| 42 | /// Controls whether execution is skipped if the output file is up-to-date. | ||
| 43 | /// The default is to always run if there is no output file, and to skip | ||
| 44 | /// running if all output files are up-to-date. | ||
| 45 | condition: enum { output_outdated, always } = .output_outdated, | ||
| 46 | |||
| 47 | /// Additional file paths relative to build.zig that, when modified, indicate | ||
| 48 | /// that the RunStep should be re-executed. | ||
| 49 | extra_file_dependencies: []const []const u8 = &.{}, | ||
| 42 | 50 | ||
| 43 | pub const StdIoAction = union(enum) { | 51 | pub const StdIoAction = union(enum) { |
| 44 | inherit, | 52 | inherit, |
| ... | @@ -51,6 +59,12 @@ pub const Arg = union(enum) { | ... | @@ -51,6 +59,12 @@ pub const Arg = union(enum) { |
| 51 | artifact: *CompileStep, | 59 | artifact: *CompileStep, |
| 52 | file_source: std.Build.FileSource, | 60 | file_source: std.Build.FileSource, |
| 53 | bytes: []u8, | 61 | bytes: []u8, |
| 62 | output: Output, | ||
| 63 | |||
| 64 | pub const Output = struct { | ||
| 65 | generated_file: *std.Build.GeneratedFile, | ||
| 66 | basename: []const u8, | ||
| 67 | }; | ||
| 54 | }; | 68 | }; |
| 55 | 69 | ||
| 56 | pub fn create(builder: *std.Build, name: []const u8) *RunStep { | 70 | pub fn create(builder: *std.Build, name: []const u8) *RunStep { |
| ... | @@ -71,6 +85,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { | ... | @@ -71,6 +85,20 @@ pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { |
| 71 | self.step.dependOn(&artifact.step); | 85 | self.step.dependOn(&artifact.step); |
| 72 | } | 86 | } |
| 73 | 87 | ||
| 88 | /// This provides file path as a command line argument to the command being | ||
| 89 | /// run, and returns a FileSource which can be used as inputs to other APIs | ||
| 90 | /// throughout the build system. | ||
| 91 | pub fn addOutputFileArg(rs: *RunStep, basename: []const u8) std.Build.FileSource { | ||
| 92 | const generated_file = rs.builder.allocator.create(std.Build.GeneratedFile) catch @panic("OOM"); | ||
| 93 | generated_file.* = .{ .step = &rs.step }; | ||
| 94 | rs.argv.append(.{ .output = .{ | ||
| 95 | .generated_file = generated_file, | ||
| 96 | .basename = rs.builder.dupe(basename), | ||
| 97 | } }) catch @panic("OOM"); | ||
| 98 | |||
| 99 | return .{ .generated = generated_file }; | ||
| 100 | } | ||
| 101 | |||
| 74 | pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { | 102 | pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { |
| 75 | self.argv.append(Arg{ | 103 | self.argv.append(Arg{ |
| 76 | .file_source = file_source.dupe(self.builder), | 104 | .file_source = file_source.dupe(self.builder), |
| ... | @@ -159,25 +187,105 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { | ... | @@ -159,25 +187,105 @@ fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { |
| 159 | }; | 187 | }; |
| 160 | } | 188 | } |
| 161 | 189 | ||
| 190 | fn needOutputCheck(self: RunStep) bool { | ||
| 191 | if (self.extra_file_dependencies.len > 0) return true; | ||
| 192 | |||
| 193 | for (self.argv.items) |arg| switch (arg) { | ||
| 194 | .output => return true, | ||
| 195 | else => continue, | ||
| 196 | }; | ||
| 197 | |||
| 198 | return switch (self.condition) { | ||
| 199 | .always => false, | ||
| 200 | .output_outdated => true, | ||
| 201 | }; | ||
| 202 | } | ||
| 203 | |||
| 162 | fn make(step: *Step) !void { | 204 | fn make(step: *Step) !void { |
| 163 | const self = @fieldParentPtr(RunStep, "step", step); | 205 | const self = @fieldParentPtr(RunStep, "step", step); |
| 206 | const need_output_check = self.needOutputCheck(); | ||
| 164 | 207 | ||
| 165 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | 208 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); |
| 209 | var output_placeholders = ArrayList(struct { | ||
| 210 | index: usize, | ||
| 211 | output: Arg.Output, | ||
| 212 | }).init(self.builder.allocator); | ||
| 213 | |||
| 214 | var man = self.builder.cache.obtain(); | ||
| 215 | defer man.deinit(); | ||
| 216 | |||
| 166 | for (self.argv.items) |arg| { | 217 | for (self.argv.items) |arg| { |
| 167 | switch (arg) { | 218 | switch (arg) { |
| 168 | .bytes => |bytes| try argv_list.append(bytes), | 219 | .bytes => |bytes| { |
| 169 | .file_source => |file| try argv_list.append(file.getPath(self.builder)), | 220 | try argv_list.append(bytes); |
| 221 | man.hash.addBytes(bytes); | ||
| 222 | }, | ||
| 223 | .file_source => |file| { | ||
| 224 | const file_path = file.getPath(self.builder); | ||
| 225 | try argv_list.append(file_path); | ||
| 226 | _ = try man.addFile(file_path, null); | ||
| 227 | }, | ||
| 170 | .artifact => |artifact| { | 228 | .artifact => |artifact| { |
| 171 | if (artifact.target.isWindows()) { | 229 | if (artifact.target.isWindows()) { |
| 172 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | 230 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH |
| 173 | self.addPathForDynLibs(artifact); | 231 | self.addPathForDynLibs(artifact); |
| 174 | } | 232 | } |
| 175 | const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder); | 233 | const file_path = artifact.installed_path orelse |
| 176 | try argv_list.append(executable_path); | 234 | artifact.getOutputSource().getPath(self.builder); |
| 235 | |||
| 236 | try argv_list.append(file_path); | ||
| 237 | |||
| 238 | _ = try man.addFile(file_path, null); | ||
| 239 | }, | ||
| 240 | .output => |output| { | ||
| 241 | man.hash.addBytes(output.basename); | ||
| 242 | // Add a placeholder into the argument list because we need the | ||
| 243 | // manifest hash to be updated with all arguments before the | ||
| 244 | // object directory is computed. | ||
| 245 | try argv_list.append(""); | ||
| 246 | try output_placeholders.append(.{ | ||
| 247 | .index = argv_list.items.len - 1, | ||
| 248 | .output = output, | ||
| 249 | }); | ||
| 177 | }, | 250 | }, |
| 178 | } | 251 | } |
| 179 | } | 252 | } |
| 180 | 253 | ||
| 254 | if (need_output_check) { | ||
| 255 | for (self.extra_file_dependencies) |file_path| { | ||
| 256 | _ = try man.addFile(self.builder.pathFromRoot(file_path), null); | ||
| 257 | } | ||
| 258 | |||
| 259 | if (man.hit() catch |err| failWithCacheError(man, err)) { | ||
| 260 | // cache hit, skip running command | ||
| 261 | const digest = man.final(); | ||
| 262 | for (output_placeholders.items) |placeholder| { | ||
| 263 | placeholder.output.generated_file.path = try self.builder.cache_root.join( | ||
| 264 | self.builder.allocator, | ||
| 265 | &.{ "o", &digest, placeholder.output.basename }, | ||
| 266 | ); | ||
| 267 | } | ||
| 268 | return; | ||
| 269 | } | ||
| 270 | |||
| 271 | const digest = man.final(); | ||
| 272 | |||
| 273 | for (output_placeholders.items) |placeholder| { | ||
| 274 | const output_path = try self.builder.cache_root.join( | ||
| 275 | self.builder.allocator, | ||
| 276 | &.{ "o", &digest, placeholder.output.basename }, | ||
| 277 | ); | ||
| 278 | const output_dir = fs.path.dirname(output_path).?; | ||
| 279 | fs.cwd().makePath(output_dir) catch |err| { | ||
| 280 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); | ||
| 281 | return err; | ||
| 282 | }; | ||
| 283 | |||
| 284 | placeholder.output.generated_file.path = output_path; | ||
| 285 | argv_list.items[placeholder.index] = output_path; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 181 | try runCommand( | 289 | try runCommand( |
| 182 | argv_list.items, | 290 | argv_list.items, |
| 183 | self.builder, | 291 | self.builder, |
| ... | @@ -189,6 +297,10 @@ fn make(step: *Step) !void { | ... | @@ -189,6 +297,10 @@ fn make(step: *Step) !void { |
| 189 | self.cwd, | 297 | self.cwd, |
| 190 | self.print, | 298 | self.print, |
| 191 | ); | 299 | ); |
| 300 | |||
| 301 | if (need_output_check) { | ||
| 302 | try man.writeManifest(); | ||
| 303 | } | ||
| 192 | } | 304 | } |
| 193 | 305 | ||
| 194 | pub fn runCommand( | 306 | pub fn runCommand( |
| ... | @@ -202,11 +314,13 @@ pub fn runCommand( | ... | @@ -202,11 +314,13 @@ pub fn runCommand( |
| 202 | maybe_cwd: ?[]const u8, | 314 | maybe_cwd: ?[]const u8, |
| 203 | print: bool, | 315 | print: bool, |
| 204 | ) !void { | 316 | ) !void { |
| 205 | const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root; | 317 | const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root.path; |
| 206 | 318 | ||
| 207 | if (!std.process.can_spawn) { | 319 | if (!std.process.can_spawn) { |
| 208 | const cmd = try std.mem.join(builder.allocator, " ", argv); | 320 | const cmd = try std.mem.join(builder.allocator, " ", argv); |
| 209 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); | 321 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ |
| 322 | @tagName(builtin.os.tag), cmd, | ||
| 323 | }); | ||
| 210 | builder.allocator.free(cmd); | 324 | builder.allocator.free(cmd); |
| 211 | return ExecError.ExecNotSupported; | 325 | return ExecError.ExecNotSupported; |
| 212 | } | 326 | } |
| ... | @@ -347,6 +461,19 @@ pub fn runCommand( | ... | @@ -347,6 +461,19 @@ pub fn runCommand( |
| 347 | } | 461 | } |
| 348 | } | 462 | } |
| 349 | 463 | ||
| 464 | fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { | ||
| 465 | const i = man.failed_file_index orelse failWithSimpleError(err); | ||
| 466 | const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); | ||
| 467 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; | ||
| 468 | std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); | ||
| 469 | std.process.exit(1); | ||
| 470 | } | ||
| 471 | |||
| 472 | fn failWithSimpleError(err: anyerror) noreturn { | ||
| 473 | std.debug.print("{s}\n", .{@errorName(err)}); | ||
| 474 | std.process.exit(1); | ||
| 475 | } | ||
| 476 | |||
| 350 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | 477 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { |
| 351 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | 478 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); |
| 352 | for (argv) |arg| { | 479 | for (argv) |arg| { |
lib/std/Build/TranslateCStep.zig+2-9| ... | @@ -15,7 +15,6 @@ builder: *std.Build, | ... | @@ -15,7 +15,6 @@ builder: *std.Build, |
| 15 | source: std.Build.FileSource, | 15 | source: std.Build.FileSource, |
| 16 | include_dirs: std.ArrayList([]const u8), | 16 | include_dirs: std.ArrayList([]const u8), |
| 17 | c_macros: std.ArrayList([]const u8), | 17 | c_macros: std.ArrayList([]const u8), |
| 18 | output_dir: ?[]const u8, | ||
| 19 | out_basename: []const u8, | 18 | out_basename: []const u8, |
| 20 | target: CrossTarget, | 19 | target: CrossTarget, |
| 21 | optimize: std.builtin.OptimizeMode, | 20 | optimize: std.builtin.OptimizeMode, |
| ... | @@ -36,7 +35,6 @@ pub fn create(builder: *std.Build, options: Options) *TranslateCStep { | ... | @@ -36,7 +35,6 @@ pub fn create(builder: *std.Build, options: Options) *TranslateCStep { |
| 36 | .source = source, | 35 | .source = source, |
| 37 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), | 36 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), |
| 38 | .c_macros = std.ArrayList([]const u8).init(builder.allocator), | 37 | .c_macros = std.ArrayList([]const u8).init(builder.allocator), |
| 39 | .output_dir = null, | ||
| 40 | .out_basename = undefined, | 38 | .out_basename = undefined, |
| 41 | .target = options.target, | 39 | .target = options.target, |
| 42 | .optimize = options.optimize, | 40 | .optimize = options.optimize, |
| ... | @@ -122,15 +120,10 @@ fn make(step: *Step) !void { | ... | @@ -122,15 +120,10 @@ fn make(step: *Step) !void { |
| 122 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | 120 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); |
| 123 | 121 | ||
| 124 | self.out_basename = fs.path.basename(output_path); | 122 | self.out_basename = fs.path.basename(output_path); |
| 125 | if (self.output_dir) |output_dir| { | 123 | const output_dir = fs.path.dirname(output_path).?; |
| 126 | const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename }); | ||
| 127 | try self.builder.updateFile(output_path, full_dest); | ||
| 128 | } else { | ||
| 129 | self.output_dir = fs.path.dirname(output_path).?; | ||
| 130 | } | ||
| 131 | 124 | ||
| 132 | self.output_file.path = try fs.path.join( | 125 | self.output_file.path = try fs.path.join( |
| 133 | self.builder.allocator, | 126 | self.builder.allocator, |
| 134 | &[_][]const u8{ self.output_dir.?, self.out_basename }, | 127 | &[_][]const u8{ output_dir, self.out_basename }, |
| 135 | ); | 128 | ); |
| 136 | } | 129 | } |
lib/std/Build/WriteFileStep.zig+6-8| ... | @@ -9,7 +9,6 @@ pub const base_id = .write_file; | ... | @@ -9,7 +9,6 @@ pub const base_id = .write_file; |
| 9 | 9 | ||
| 10 | step: Step, | 10 | step: Step, |
| 11 | builder: *std.Build, | 11 | builder: *std.Build, |
| 12 | output_dir: []const u8, | ||
| 13 | files: std.TailQueue(File), | 12 | files: std.TailQueue(File), |
| 14 | 13 | ||
| 15 | pub const File = struct { | 14 | pub const File = struct { |
| ... | @@ -23,7 +22,6 @@ pub fn init(builder: *std.Build) WriteFileStep { | ... | @@ -23,7 +22,6 @@ pub fn init(builder: *std.Build) WriteFileStep { |
| 23 | .builder = builder, | 22 | .builder = builder, |
| 24 | .step = Step.init(.write_file, "writefile", builder.allocator, make), | 23 | .step = Step.init(.write_file, "writefile", builder.allocator, make), |
| 25 | .files = .{}, | 24 | .files = .{}, |
| 26 | .output_dir = undefined, | ||
| 27 | }; | 25 | }; |
| 28 | } | 26 | } |
| 29 | 27 | ||
| ... | @@ -87,11 +85,11 @@ fn make(step: *Step) !void { | ... | @@ -87,11 +85,11 @@ fn make(step: *Step) !void { |
| 87 | .{std.fmt.fmtSliceHexLower(&digest)}, | 85 | .{std.fmt.fmtSliceHexLower(&digest)}, |
| 88 | ) catch unreachable; | 86 | ) catch unreachable; |
| 89 | 87 | ||
| 90 | self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{ | 88 | const output_dir = try self.builder.cache_root.join(self.builder.allocator, &.{ |
| 91 | self.builder.cache_root, "o", &hash_basename, | 89 | "o", &hash_basename, |
| 92 | }); | 90 | }); |
| 93 | var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| { | 91 | var dir = fs.cwd().makeOpenPath(output_dir, .{}) catch |err| { |
| 94 | std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | 92 | std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); |
| 95 | return err; | 93 | return err; |
| 96 | }; | 94 | }; |
| 97 | defer dir.close(); | 95 | defer dir.close(); |
| ... | @@ -101,14 +99,14 @@ fn make(step: *Step) !void { | ... | @@ -101,14 +99,14 @@ fn make(step: *Step) !void { |
| 101 | dir.writeFile(node.data.basename, node.data.bytes) catch |err| { | 99 | dir.writeFile(node.data.basename, node.data.bytes) catch |err| { |
| 102 | std.debug.print("unable to write {s} into {s}: {s}\n", .{ | 100 | std.debug.print("unable to write {s} into {s}: {s}\n", .{ |
| 103 | node.data.basename, | 101 | node.data.basename, |
| 104 | self.output_dir, | 102 | output_dir, |
| 105 | @errorName(err), | 103 | @errorName(err), |
| 106 | }); | 104 | }); |
| 107 | return err; | 105 | return err; |
| 108 | }; | 106 | }; |
| 109 | node.data.source.path = try fs.path.join( | 107 | node.data.source.path = try fs.path.join( |
| 110 | self.builder.allocator, | 108 | self.builder.allocator, |
| 111 | &[_][]const u8{ self.output_dir, node.data.basename }, | 109 | &[_][]const u8{ output_dir, node.data.basename }, |
| 112 | ); | 110 | ); |
| 113 | } | 111 | } |
| 114 | } | 112 | } |
lib/std/c.zig+1| ... | @@ -173,6 +173,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs | ... | @@ -173,6 +173,7 @@ pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufs |
| 173 | pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; | 173 | pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize; |
| 174 | pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int; | 174 | pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int; |
| 175 | pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int; | 175 | pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int; |
| 176 | pub extern "c" fn umask(mode: c.mode_t) c.mode_t; | ||
| 176 | 177 | ||
| 177 | pub extern "c" fn rmdir(path: [*:0]const u8) c_int; | 178 | pub extern "c" fn rmdir(path: [*:0]const u8) c_int; |
| 178 | pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8; | 179 | pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8; |
lib/std/child_process.zig+2-2| ... | @@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt( |
| 1164 | var app_name_unicode_string = windows.UNICODE_STRING{ | 1164 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1165 | .Length = app_name_len_bytes, | 1165 | .Length = app_name_len_bytes, |
| 1166 | .MaximumLength = app_name_len_bytes, | 1166 | .MaximumLength = app_name_len_bytes, |
| 1167 | .Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr), | 1167 | .Buffer = @constCast(app_name_wildcard.ptr), |
| 1168 | }; | 1168 | }; |
| 1169 | const rc = windows.ntdll.NtQueryDirectoryFile( | 1169 | const rc = windows.ntdll.NtQueryDirectoryFile( |
| 1170 | dir.fd, | 1170 | dir.fd, |
| ... | @@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt( |
| 1261 | var app_name_unicode_string = windows.UNICODE_STRING{ | 1261 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1262 | .Length = app_name_len_bytes, | 1262 | .Length = app_name_len_bytes, |
| 1263 | .MaximumLength = app_name_len_bytes, | 1263 | .MaximumLength = app_name_len_bytes, |
| 1264 | .Buffer = @qualCast([*:0]u16, app_name_appended.ptr), | 1264 | .Buffer = @constCast(app_name_appended.ptr), |
| 1265 | }; | 1265 | }; |
| 1266 | 1266 | ||
| 1267 | // Re-use the directory handle but this time we call with the appended app name | 1267 | // Re-use the directory handle but this time we call with the appended app name |
lib/std/compress.zig+6-2| ... | @@ -2,8 +2,10 @@ const std = @import("std.zig"); | ... | @@ -2,8 +2,10 @@ const std = @import("std.zig"); |
| 2 | 2 | ||
| 3 | pub const deflate = @import("compress/deflate.zig"); | 3 | pub const deflate = @import("compress/deflate.zig"); |
| 4 | pub const gzip = @import("compress/gzip.zig"); | 4 | pub const gzip = @import("compress/gzip.zig"); |
| 5 | pub const zlib = @import("compress/zlib.zig"); | 5 | pub const lzma = @import("compress/lzma.zig"); |
| 6 | pub const lzma2 = @import("compress/lzma2.zig"); | ||
| 6 | pub const xz = @import("compress/xz.zig"); | 7 | pub const xz = @import("compress/xz.zig"); |
| 8 | pub const zlib = @import("compress/zlib.zig"); | ||
| 7 | 9 | ||
| 8 | pub fn HashedReader( | 10 | pub fn HashedReader( |
| 9 | comptime ReaderType: anytype, | 11 | comptime ReaderType: anytype, |
| ... | @@ -38,6 +40,8 @@ pub fn hashedReader( | ... | @@ -38,6 +40,8 @@ pub fn hashedReader( |
| 38 | test { | 40 | test { |
| 39 | _ = deflate; | 41 | _ = deflate; |
| 40 | _ = gzip; | 42 | _ = gzip; |
| 41 | _ = zlib; | 43 | _ = lzma; |
| 44 | _ = lzma2; | ||
| 42 | _ = xz; | 45 | _ = xz; |
| 46 | _ = zlib; | ||
| 43 | } | 47 | } |
lib/std/compress/lzma.zig created+90| ... | @@ -0,0 +1,90 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const math = std.math; | ||
| 3 | const mem = std.mem; | ||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | |||
| 6 | pub const decode = @import("lzma/decode.zig"); | ||
| 7 | |||
| 8 | pub fn decompress( | ||
| 9 | allocator: Allocator, | ||
| 10 | reader: anytype, | ||
| 11 | ) !Decompress(@TypeOf(reader)) { | ||
| 12 | return decompressWithOptions(allocator, reader, .{}); | ||
| 13 | } | ||
| 14 | |||
| 15 | pub fn decompressWithOptions( | ||
| 16 | allocator: Allocator, | ||
| 17 | reader: anytype, | ||
| 18 | options: decode.Options, | ||
| 19 | ) !Decompress(@TypeOf(reader)) { | ||
| 20 | const params = try decode.Params.readHeader(reader, options); | ||
| 21 | return Decompress(@TypeOf(reader)).init(allocator, reader, params, options.memlimit); | ||
| 22 | } | ||
| 23 | |||
| 24 | pub fn Decompress(comptime ReaderType: type) type { | ||
| 25 | return struct { | ||
| 26 | const Self = @This(); | ||
| 27 | |||
| 28 | pub const Error = | ||
| 29 | ReaderType.Error || | ||
| 30 | Allocator.Error || | ||
| 31 | error{ CorruptInput, EndOfStream, Overflow }; | ||
| 32 | |||
| 33 | pub const Reader = std.io.Reader(*Self, Error, read); | ||
| 34 | |||
| 35 | allocator: Allocator, | ||
| 36 | in_reader: ReaderType, | ||
| 37 | to_read: std.ArrayListUnmanaged(u8), | ||
| 38 | |||
| 39 | buffer: decode.lzbuffer.LzCircularBuffer, | ||
| 40 | decoder: decode.rangecoder.RangeDecoder, | ||
| 41 | state: decode.DecoderState, | ||
| 42 | |||
| 43 | pub fn init(allocator: Allocator, source: ReaderType, params: decode.Params, memlimit: ?usize) !Self { | ||
| 44 | return Self{ | ||
| 45 | .allocator = allocator, | ||
| 46 | .in_reader = source, | ||
| 47 | .to_read = .{}, | ||
| 48 | |||
| 49 | .buffer = decode.lzbuffer.LzCircularBuffer.init(params.dict_size, memlimit orelse math.maxInt(usize)), | ||
| 50 | .decoder = try decode.rangecoder.RangeDecoder.init(source), | ||
| 51 | .state = try decode.DecoderState.init(allocator, params.properties, params.unpacked_size), | ||
| 52 | }; | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn reader(self: *Self) Reader { | ||
| 56 | return .{ .context = self }; | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn deinit(self: *Self) void { | ||
| 60 | self.to_read.deinit(self.allocator); | ||
| 61 | self.buffer.deinit(self.allocator); | ||
| 62 | self.state.deinit(self.allocator); | ||
| 63 | self.* = undefined; | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn read(self: *Self, output: []u8) Error!usize { | ||
| 67 | const writer = self.to_read.writer(self.allocator); | ||
| 68 | while (self.to_read.items.len < output.len) { | ||
| 69 | switch (try self.state.process(self.allocator, self.in_reader, writer, &self.buffer, &self.decoder)) { | ||
| 70 | .continue_ => {}, | ||
| 71 | .finished => { | ||
| 72 | try self.buffer.finish(writer); | ||
| 73 | break; | ||
| 74 | }, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | const input = self.to_read.items; | ||
| 78 | const n = math.min(input.len, output.len); | ||
| 79 | mem.copy(u8, output[0..n], input[0..n]); | ||
| 80 | mem.copy(u8, input, input[n..]); | ||
| 81 | self.to_read.shrinkRetainingCapacity(input.len - n); | ||
| 82 | return n; | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | |||
| 87 | test { | ||
| 88 | _ = @import("lzma/test.zig"); | ||
| 89 | _ = @import("lzma/vec2d.zig"); | ||
| 90 | } | ||
lib/std/compress/lzma/decode.zig created+379| ... | @@ -0,0 +1,379 @@ | ||
| 1 | const std = @import("../../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const math = std.math; | ||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | |||
| 6 | pub const lzbuffer = @import("decode/lzbuffer.zig"); | ||
| 7 | pub const rangecoder = @import("decode/rangecoder.zig"); | ||
| 8 | |||
| 9 | const LzCircularBuffer = lzbuffer.LzCircularBuffer; | ||
| 10 | const BitTree = rangecoder.BitTree; | ||
| 11 | const LenDecoder = rangecoder.LenDecoder; | ||
| 12 | const RangeDecoder = rangecoder.RangeDecoder; | ||
| 13 | const Vec2D = @import("vec2d.zig").Vec2D; | ||
| 14 | |||
| 15 | pub const Options = struct { | ||
| 16 | unpacked_size: UnpackedSize = .read_from_header, | ||
| 17 | memlimit: ?usize = null, | ||
| 18 | allow_incomplete: bool = false, | ||
| 19 | }; | ||
| 20 | |||
| 21 | pub const UnpackedSize = union(enum) { | ||
| 22 | read_from_header, | ||
| 23 | read_header_but_use_provided: ?u64, | ||
| 24 | use_provided: ?u64, | ||
| 25 | }; | ||
| 26 | |||
| 27 | const ProcessingStatus = enum { | ||
| 28 | continue_, | ||
| 29 | finished, | ||
| 30 | }; | ||
| 31 | |||
| 32 | pub const Properties = struct { | ||
| 33 | lc: u4, | ||
| 34 | lp: u3, | ||
| 35 | pb: u3, | ||
| 36 | |||
| 37 | fn validate(self: Properties) void { | ||
| 38 | assert(self.lc <= 8); | ||
| 39 | assert(self.lp <= 4); | ||
| 40 | assert(self.pb <= 4); | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | |||
| 44 | pub const Params = struct { | ||
| 45 | properties: Properties, | ||
| 46 | dict_size: u32, | ||
| 47 | unpacked_size: ?u64, | ||
| 48 | |||
| 49 | pub fn readHeader(reader: anytype, options: Options) !Params { | ||
| 50 | var props = try reader.readByte(); | ||
| 51 | if (props >= 225) { | ||
| 52 | return error.CorruptInput; | ||
| 53 | } | ||
| 54 | |||
| 55 | const lc = @intCast(u4, props % 9); | ||
| 56 | props /= 9; | ||
| 57 | const lp = @intCast(u3, props % 5); | ||
| 58 | props /= 5; | ||
| 59 | const pb = @intCast(u3, props); | ||
| 60 | |||
| 61 | const dict_size_provided = try reader.readIntLittle(u32); | ||
| 62 | const dict_size = math.max(0x1000, dict_size_provided); | ||
| 63 | |||
| 64 | const unpacked_size = switch (options.unpacked_size) { | ||
| 65 | .read_from_header => blk: { | ||
| 66 | const unpacked_size_provided = try reader.readIntLittle(u64); | ||
| 67 | const marker_mandatory = unpacked_size_provided == 0xFFFF_FFFF_FFFF_FFFF; | ||
| 68 | break :blk if (marker_mandatory) | ||
| 69 | null | ||
| 70 | else | ||
| 71 | unpacked_size_provided; | ||
| 72 | }, | ||
| 73 | .read_header_but_use_provided => |x| blk: { | ||
| 74 | _ = try reader.readIntLittle(u64); | ||
| 75 | break :blk x; | ||
| 76 | }, | ||
| 77 | .use_provided => |x| x, | ||
| 78 | }; | ||
| 79 | |||
| 80 | return Params{ | ||
| 81 | .properties = Properties{ .lc = lc, .lp = lp, .pb = pb }, | ||
| 82 | .dict_size = dict_size, | ||
| 83 | .unpacked_size = unpacked_size, | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | }; | ||
| 87 | |||
| 88 | pub const DecoderState = struct { | ||
| 89 | lzma_props: Properties, | ||
| 90 | unpacked_size: ?u64, | ||
| 91 | literal_probs: Vec2D(u16), | ||
| 92 | pos_slot_decoder: [4]BitTree(6), | ||
| 93 | align_decoder: BitTree(4), | ||
| 94 | pos_decoders: [115]u16, | ||
| 95 | is_match: [192]u16, | ||
| 96 | is_rep: [12]u16, | ||
| 97 | is_rep_g0: [12]u16, | ||
| 98 | is_rep_g1: [12]u16, | ||
| 99 | is_rep_g2: [12]u16, | ||
| 100 | is_rep_0long: [192]u16, | ||
| 101 | state: usize, | ||
| 102 | rep: [4]usize, | ||
| 103 | len_decoder: LenDecoder, | ||
| 104 | rep_len_decoder: LenDecoder, | ||
| 105 | |||
| 106 | pub fn init( | ||
| 107 | allocator: Allocator, | ||
| 108 | lzma_props: Properties, | ||
| 109 | unpacked_size: ?u64, | ||
| 110 | ) !DecoderState { | ||
| 111 | return .{ | ||
| 112 | .lzma_props = lzma_props, | ||
| 113 | .unpacked_size = unpacked_size, | ||
| 114 | .literal_probs = try Vec2D(u16).init(allocator, 0x400, .{ @as(usize, 1) << (lzma_props.lc + lzma_props.lp), 0x300 }), | ||
| 115 | .pos_slot_decoder = .{.{}} ** 4, | ||
| 116 | .align_decoder = .{}, | ||
| 117 | .pos_decoders = .{0x400} ** 115, | ||
| 118 | .is_match = .{0x400} ** 192, | ||
| 119 | .is_rep = .{0x400} ** 12, | ||
| 120 | .is_rep_g0 = .{0x400} ** 12, | ||
| 121 | .is_rep_g1 = .{0x400} ** 12, | ||
| 122 | .is_rep_g2 = .{0x400} ** 12, | ||
| 123 | .is_rep_0long = .{0x400} ** 192, | ||
| 124 | .state = 0, | ||
| 125 | .rep = .{0} ** 4, | ||
| 126 | .len_decoder = .{}, | ||
| 127 | .rep_len_decoder = .{}, | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | |||
| 131 | pub fn deinit(self: *DecoderState, allocator: Allocator) void { | ||
| 132 | self.literal_probs.deinit(allocator); | ||
| 133 | self.* = undefined; | ||
| 134 | } | ||
| 135 | |||
| 136 | pub fn resetState(self: *DecoderState, allocator: Allocator, new_props: Properties) !void { | ||
| 137 | new_props.validate(); | ||
| 138 | if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) { | ||
| 139 | self.literal_probs.fill(0x400); | ||
| 140 | } else { | ||
| 141 | self.literal_probs.deinit(allocator); | ||
| 142 | self.literal_probs = try Vec2D(u16).init(allocator, 0x400, .{ @as(usize, 1) << (new_props.lc + new_props.lp), 0x300 }); | ||
| 143 | } | ||
| 144 | |||
| 145 | self.lzma_props = new_props; | ||
| 146 | for (self.pos_slot_decoder) |*t| t.reset(); | ||
| 147 | self.align_decoder.reset(); | ||
| 148 | self.pos_decoders = .{0x400} ** 115; | ||
| 149 | self.is_match = .{0x400} ** 192; | ||
| 150 | self.is_rep = .{0x400} ** 12; | ||
| 151 | self.is_rep_g0 = .{0x400} ** 12; | ||
| 152 | self.is_rep_g1 = .{0x400} ** 12; | ||
| 153 | self.is_rep_g2 = .{0x400} ** 12; | ||
| 154 | self.is_rep_0long = .{0x400} ** 192; | ||
| 155 | self.state = 0; | ||
| 156 | self.rep = .{0} ** 4; | ||
| 157 | self.len_decoder.reset(); | ||
| 158 | self.rep_len_decoder.reset(); | ||
| 159 | } | ||
| 160 | |||
| 161 | fn processNextInner( | ||
| 162 | self: *DecoderState, | ||
| 163 | allocator: Allocator, | ||
| 164 | reader: anytype, | ||
| 165 | writer: anytype, | ||
| 166 | buffer: anytype, | ||
| 167 | decoder: *RangeDecoder, | ||
| 168 | update: bool, | ||
| 169 | ) !ProcessingStatus { | ||
| 170 | const pos_state = buffer.len & ((@as(usize, 1) << self.lzma_props.pb) - 1); | ||
| 171 | |||
| 172 | if (!try decoder.decodeBit( | ||
| 173 | reader, | ||
| 174 | &self.is_match[(self.state << 4) + pos_state], | ||
| 175 | update, | ||
| 176 | )) { | ||
| 177 | const byte: u8 = try self.decodeLiteral(reader, buffer, decoder, update); | ||
| 178 | |||
| 179 | if (update) { | ||
| 180 | try buffer.appendLiteral(allocator, byte, writer); | ||
| 181 | |||
| 182 | self.state = if (self.state < 4) | ||
| 183 | 0 | ||
| 184 | else if (self.state < 10) | ||
| 185 | self.state - 3 | ||
| 186 | else | ||
| 187 | self.state - 6; | ||
| 188 | } | ||
| 189 | return .continue_; | ||
| 190 | } | ||
| 191 | |||
| 192 | var len: usize = undefined; | ||
| 193 | if (try decoder.decodeBit(reader, &self.is_rep[self.state], update)) { | ||
| 194 | if (!try decoder.decodeBit(reader, &self.is_rep_g0[self.state], update)) { | ||
| 195 | if (!try decoder.decodeBit( | ||
| 196 | reader, | ||
| 197 | &self.is_rep_0long[(self.state << 4) + pos_state], | ||
| 198 | update, | ||
| 199 | )) { | ||
| 200 | if (update) { | ||
| 201 | self.state = if (self.state < 7) 9 else 11; | ||
| 202 | const dist = self.rep[0] + 1; | ||
| 203 | try buffer.appendLz(allocator, 1, dist, writer); | ||
| 204 | } | ||
| 205 | return .continue_; | ||
| 206 | } | ||
| 207 | } else { | ||
| 208 | const idx: usize = if (!try decoder.decodeBit(reader, &self.is_rep_g1[self.state], update)) | ||
| 209 | 1 | ||
| 210 | else if (!try decoder.decodeBit(reader, &self.is_rep_g2[self.state], update)) | ||
| 211 | 2 | ||
| 212 | else | ||
| 213 | 3; | ||
| 214 | if (update) { | ||
| 215 | const dist = self.rep[idx]; | ||
| 216 | var i = idx; | ||
| 217 | while (i > 0) : (i -= 1) { | ||
| 218 | self.rep[i] = self.rep[i - 1]; | ||
| 219 | } | ||
| 220 | self.rep[0] = dist; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | len = try self.rep_len_decoder.decode(reader, decoder, pos_state, update); | ||
| 225 | |||
| 226 | if (update) { | ||
| 227 | self.state = if (self.state < 7) 8 else 11; | ||
| 228 | } | ||
| 229 | } else { | ||
| 230 | if (update) { | ||
| 231 | self.rep[3] = self.rep[2]; | ||
| 232 | self.rep[2] = self.rep[1]; | ||
| 233 | self.rep[1] = self.rep[0]; | ||
| 234 | } | ||
| 235 | |||
| 236 | len = try self.len_decoder.decode(reader, decoder, pos_state, update); | ||
| 237 | |||
| 238 | if (update) { | ||
| 239 | self.state = if (self.state < 7) 7 else 10; | ||
| 240 | } | ||
| 241 | |||
| 242 | const rep_0 = try self.decodeDistance(reader, decoder, len, update); | ||
| 243 | |||
| 244 | if (update) { | ||
| 245 | self.rep[0] = rep_0; | ||
| 246 | if (self.rep[0] == 0xFFFF_FFFF) { | ||
| 247 | if (decoder.isFinished()) { | ||
| 248 | return .finished; | ||
| 249 | } | ||
| 250 | return error.CorruptInput; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | if (update) { | ||
| 256 | len += 2; | ||
| 257 | |||
| 258 | const dist = self.rep[0] + 1; | ||
| 259 | try buffer.appendLz(allocator, len, dist, writer); | ||
| 260 | } | ||
| 261 | |||
| 262 | return .continue_; | ||
| 263 | } | ||
| 264 | |||
| 265 | fn processNext( | ||
| 266 | self: *DecoderState, | ||
| 267 | allocator: Allocator, | ||
| 268 | reader: anytype, | ||
| 269 | writer: anytype, | ||
| 270 | buffer: anytype, | ||
| 271 | decoder: *RangeDecoder, | ||
| 272 | ) !ProcessingStatus { | ||
| 273 | return self.processNextInner(allocator, reader, writer, buffer, decoder, true); | ||
| 274 | } | ||
| 275 | |||
| 276 | pub fn process( | ||
| 277 | self: *DecoderState, | ||
| 278 | allocator: Allocator, | ||
| 279 | reader: anytype, | ||
| 280 | writer: anytype, | ||
| 281 | buffer: anytype, | ||
| 282 | decoder: *RangeDecoder, | ||
| 283 | ) !ProcessingStatus { | ||
| 284 | process_next: { | ||
| 285 | if (self.unpacked_size) |unpacked_size| { | ||
| 286 | if (buffer.len >= unpacked_size) { | ||
| 287 | break :process_next; | ||
| 288 | } | ||
| 289 | } else if (decoder.isFinished()) { | ||
| 290 | break :process_next; | ||
| 291 | } | ||
| 292 | |||
| 293 | switch (try self.processNext(allocator, reader, writer, buffer, decoder)) { | ||
| 294 | .continue_ => return .continue_, | ||
| 295 | .finished => break :process_next, | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | if (self.unpacked_size) |unpacked_size| { | ||
| 300 | if (buffer.len != unpacked_size) { | ||
| 301 | return error.CorruptInput; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | return .finished; | ||
| 306 | } | ||
| 307 | |||
| 308 | fn decodeLiteral( | ||
| 309 | self: *DecoderState, | ||
| 310 | reader: anytype, | ||
| 311 | buffer: anytype, | ||
| 312 | decoder: *RangeDecoder, | ||
| 313 | update: bool, | ||
| 314 | ) !u8 { | ||
| 315 | const def_prev_byte = 0; | ||
| 316 | const prev_byte = @as(usize, buffer.lastOr(def_prev_byte)); | ||
| 317 | |||
| 318 | var result: usize = 1; | ||
| 319 | const lit_state = ((buffer.len & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) + | ||
| 320 | (prev_byte >> (8 - self.lzma_props.lc)); | ||
| 321 | const probs = try self.literal_probs.getMut(lit_state); | ||
| 322 | |||
| 323 | if (self.state >= 7) { | ||
| 324 | var match_byte = @as(usize, try buffer.lastN(self.rep[0] + 1)); | ||
| 325 | |||
| 326 | while (result < 0x100) { | ||
| 327 | const match_bit = (match_byte >> 7) & 1; | ||
| 328 | match_byte <<= 1; | ||
| 329 | const bit = @boolToInt(try decoder.decodeBit( | ||
| 330 | reader, | ||
| 331 | &probs[((@as(usize, 1) + match_bit) << 8) + result], | ||
| 332 | update, | ||
| 333 | )); | ||
| 334 | result = (result << 1) ^ bit; | ||
| 335 | if (match_bit != bit) { | ||
| 336 | break; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | while (result < 0x100) { | ||
| 342 | result = (result << 1) ^ @boolToInt(try decoder.decodeBit(reader, &probs[result], update)); | ||
| 343 | } | ||
| 344 | |||
| 345 | return @truncate(u8, result - 0x100); | ||
| 346 | } | ||
| 347 | |||
| 348 | fn decodeDistance( | ||
| 349 | self: *DecoderState, | ||
| 350 | reader: anytype, | ||
| 351 | decoder: *RangeDecoder, | ||
| 352 | length: usize, | ||
| 353 | update: bool, | ||
| 354 | ) !usize { | ||
| 355 | const len_state = if (length > 3) 3 else length; | ||
| 356 | |||
| 357 | const pos_slot = @as(usize, try self.pos_slot_decoder[len_state].parse(reader, decoder, update)); | ||
| 358 | if (pos_slot < 4) | ||
| 359 | return pos_slot; | ||
| 360 | |||
| 361 | const num_direct_bits = @intCast(u5, (pos_slot >> 1) - 1); | ||
| 362 | var result = (2 ^ (pos_slot & 1)) << num_direct_bits; | ||
| 363 | |||
| 364 | if (pos_slot < 14) { | ||
| 365 | result += try decoder.parseReverseBitTree( | ||
| 366 | reader, | ||
| 367 | num_direct_bits, | ||
| 368 | &self.pos_decoders, | ||
| 369 | result - pos_slot, | ||
| 370 | update, | ||
| 371 | ); | ||
| 372 | } else { | ||
| 373 | result += @as(usize, try decoder.get(reader, num_direct_bits - 4)) << 4; | ||
| 374 | result += try self.align_decoder.parseReverse(reader, decoder, update); | ||
| 375 | } | ||
| 376 | |||
| 377 | return result; | ||
| 378 | } | ||
| 379 | }; | ||
lib/std/compress/lzma/decode/lzbuffer.zig created+228| ... | @@ -0,0 +1,228 @@ | ||
| 1 | const std = @import("../../../std.zig"); | ||
| 2 | const math = std.math; | ||
| 3 | const mem = std.mem; | ||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 6 | |||
| 7 | /// An accumulating buffer for LZ sequences | ||
| 8 | pub const LzAccumBuffer = struct { | ||
| 9 | /// Buffer | ||
| 10 | buf: ArrayListUnmanaged(u8), | ||
| 11 | |||
| 12 | /// Buffer memory limit | ||
| 13 | memlimit: usize, | ||
| 14 | |||
| 15 | /// Total number of bytes sent through the buffer | ||
| 16 | len: usize, | ||
| 17 | |||
| 18 | const Self = @This(); | ||
| 19 | |||
| 20 | pub fn init(memlimit: usize) Self { | ||
| 21 | return Self{ | ||
| 22 | .buf = .{}, | ||
| 23 | .memlimit = memlimit, | ||
| 24 | .len = 0, | ||
| 25 | }; | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn appendByte(self: *Self, allocator: Allocator, byte: u8) !void { | ||
| 29 | try self.buf.append(allocator, byte); | ||
| 30 | self.len += 1; | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Reset the internal dictionary | ||
| 34 | pub fn reset(self: *Self, writer: anytype) !void { | ||
| 35 | try writer.writeAll(self.buf.items); | ||
| 36 | self.buf.clearRetainingCapacity(); | ||
| 37 | self.len = 0; | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Retrieve the last byte or return a default | ||
| 41 | pub fn lastOr(self: Self, lit: u8) u8 { | ||
| 42 | const buf_len = self.buf.items.len; | ||
| 43 | return if (buf_len == 0) | ||
| 44 | lit | ||
| 45 | else | ||
| 46 | self.buf.items[buf_len - 1]; | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Retrieve the n-th last byte | ||
| 50 | pub fn lastN(self: Self, dist: usize) !u8 { | ||
| 51 | const buf_len = self.buf.items.len; | ||
| 52 | if (dist > buf_len) { | ||
| 53 | return error.CorruptInput; | ||
| 54 | } | ||
| 55 | |||
| 56 | return self.buf.items[buf_len - dist]; | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Append a literal | ||
| 60 | pub fn appendLiteral( | ||
| 61 | self: *Self, | ||
| 62 | allocator: Allocator, | ||
| 63 | lit: u8, | ||
| 64 | writer: anytype, | ||
| 65 | ) !void { | ||
| 66 | _ = writer; | ||
| 67 | if (self.len >= self.memlimit) { | ||
| 68 | return error.CorruptInput; | ||
| 69 | } | ||
| 70 | try self.buf.append(allocator, lit); | ||
| 71 | self.len += 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Fetch an LZ sequence (length, distance) from inside the buffer | ||
| 75 | pub fn appendLz( | ||
| 76 | self: *Self, | ||
| 77 | allocator: Allocator, | ||
| 78 | len: usize, | ||
| 79 | dist: usize, | ||
| 80 | writer: anytype, | ||
| 81 | ) !void { | ||
| 82 | _ = writer; | ||
| 83 | |||
| 84 | const buf_len = self.buf.items.len; | ||
| 85 | if (dist > buf_len) { | ||
| 86 | return error.CorruptInput; | ||
| 87 | } | ||
| 88 | |||
| 89 | var offset = buf_len - dist; | ||
| 90 | var i: usize = 0; | ||
| 91 | while (i < len) : (i += 1) { | ||
| 92 | const x = self.buf.items[offset]; | ||
| 93 | try self.buf.append(allocator, x); | ||
| 94 | offset += 1; | ||
| 95 | } | ||
| 96 | self.len += len; | ||
| 97 | } | ||
| 98 | |||
| 99 | pub fn finish(self: *Self, writer: anytype) !void { | ||
| 100 | try writer.writeAll(self.buf.items); | ||
| 101 | self.buf.clearRetainingCapacity(); | ||
| 102 | } | ||
| 103 | |||
| 104 | pub fn deinit(self: *Self, allocator: Allocator) void { | ||
| 105 | self.buf.deinit(allocator); | ||
| 106 | self.* = undefined; | ||
| 107 | } | ||
| 108 | }; | ||
| 109 | |||
| 110 | /// A circular buffer for LZ sequences | ||
| 111 | pub const LzCircularBuffer = struct { | ||
| 112 | /// Circular buffer | ||
| 113 | buf: ArrayListUnmanaged(u8), | ||
| 114 | |||
| 115 | /// Length of the buffer | ||
| 116 | dict_size: usize, | ||
| 117 | |||
| 118 | /// Buffer memory limit | ||
| 119 | memlimit: usize, | ||
| 120 | |||
| 121 | /// Current position | ||
| 122 | cursor: usize, | ||
| 123 | |||
| 124 | /// Total number of bytes sent through the buffer | ||
| 125 | len: usize, | ||
| 126 | |||
| 127 | const Self = @This(); | ||
| 128 | |||
| 129 | pub fn init(dict_size: usize, memlimit: usize) Self { | ||
| 130 | return Self{ | ||
| 131 | .buf = .{}, | ||
| 132 | .dict_size = dict_size, | ||
| 133 | .memlimit = memlimit, | ||
| 134 | .cursor = 0, | ||
| 135 | .len = 0, | ||
| 136 | }; | ||
| 137 | } | ||
| 138 | |||
| 139 | pub fn get(self: Self, index: usize) u8 { | ||
| 140 | return if (0 <= index and index < self.buf.items.len) | ||
| 141 | self.buf.items[index] | ||
| 142 | else | ||
| 143 | 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | pub fn set(self: *Self, allocator: Allocator, index: usize, value: u8) !void { | ||
| 147 | if (index >= self.memlimit) { | ||
| 148 | return error.CorruptInput; | ||
| 149 | } | ||
| 150 | try self.buf.ensureTotalCapacity(allocator, index + 1); | ||
| 151 | while (self.buf.items.len < index) { | ||
| 152 | self.buf.appendAssumeCapacity(0); | ||
| 153 | } | ||
| 154 | self.buf.appendAssumeCapacity(value); | ||
| 155 | } | ||
| 156 | |||
| 157 | /// Retrieve the last byte or return a default | ||
| 158 | pub fn lastOr(self: Self, lit: u8) u8 { | ||
| 159 | return if (self.len == 0) | ||
| 160 | lit | ||
| 161 | else | ||
| 162 | self.get((self.dict_size + self.cursor - 1) % self.dict_size); | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Retrieve the n-th last byte | ||
| 166 | pub fn lastN(self: Self, dist: usize) !u8 { | ||
| 167 | if (dist > self.dict_size or dist > self.len) { | ||
| 168 | return error.CorruptInput; | ||
| 169 | } | ||
| 170 | |||
| 171 | const offset = (self.dict_size + self.cursor - dist) % self.dict_size; | ||
| 172 | return self.get(offset); | ||
| 173 | } | ||
| 174 | |||
| 175 | /// Append a literal | ||
| 176 | pub fn appendLiteral( | ||
| 177 | self: *Self, | ||
| 178 | allocator: Allocator, | ||
| 179 | lit: u8, | ||
| 180 | writer: anytype, | ||
| 181 | ) !void { | ||
| 182 | try self.set(allocator, self.cursor, lit); | ||
| 183 | self.cursor += 1; | ||
| 184 | self.len += 1; | ||
| 185 | |||
| 186 | // Flush the circular buffer to the output | ||
| 187 | if (self.cursor == self.dict_size) { | ||
| 188 | try writer.writeAll(self.buf.items); | ||
| 189 | self.cursor = 0; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Fetch an LZ sequence (length, distance) from inside the buffer | ||
| 194 | pub fn appendLz( | ||
| 195 | self: *Self, | ||
| 196 | allocator: Allocator, | ||
| 197 | len: usize, | ||
| 198 | dist: usize, | ||
| 199 | writer: anytype, | ||
| 200 | ) !void { | ||
| 201 | if (dist > self.dict_size or dist > self.len) { | ||
| 202 | return error.CorruptInput; | ||
| 203 | } | ||
| 204 | |||
| 205 | var offset = (self.dict_size + self.cursor - dist) % self.dict_size; | ||
| 206 | var i: usize = 0; | ||
| 207 | while (i < len) : (i += 1) { | ||
| 208 | const x = self.get(offset); | ||
| 209 | try self.appendLiteral(allocator, x, writer); | ||
| 210 | offset += 1; | ||
| 211 | if (offset == self.dict_size) { | ||
| 212 | offset = 0; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | pub fn finish(self: *Self, writer: anytype) !void { | ||
| 218 | if (self.cursor > 0) { | ||
| 219 | try writer.writeAll(self.buf.items[0..self.cursor]); | ||
| 220 | self.cursor = 0; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | pub fn deinit(self: *Self, allocator: Allocator) void { | ||
| 225 | self.buf.deinit(allocator); | ||
| 226 | self.* = undefined; | ||
| 227 | } | ||
| 228 | }; | ||
lib/std/compress/lzma/decode/rangecoder.zig created+181| ... | @@ -0,0 +1,181 @@ | ||
| 1 | const std = @import("../../../std.zig"); | ||
| 2 | const mem = std.mem; | ||
| 3 | |||
| 4 | pub const RangeDecoder = struct { | ||
| 5 | range: u32, | ||
| 6 | code: u32, | ||
| 7 | |||
| 8 | pub fn init(reader: anytype) !RangeDecoder { | ||
| 9 | const reserved = try reader.readByte(); | ||
| 10 | if (reserved != 0) { | ||
| 11 | return error.CorruptInput; | ||
| 12 | } | ||
| 13 | return RangeDecoder{ | ||
| 14 | .range = 0xFFFF_FFFF, | ||
| 15 | .code = try reader.readIntBig(u32), | ||
| 16 | }; | ||
| 17 | } | ||
| 18 | |||
| 19 | pub fn fromParts( | ||
| 20 | range: u32, | ||
| 21 | code: u32, | ||
| 22 | ) RangeDecoder { | ||
| 23 | return .{ | ||
| 24 | .range = range, | ||
| 25 | .code = code, | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | pub fn set(self: *RangeDecoder, range: u32, code: u32) void { | ||
| 30 | self.range = range; | ||
| 31 | self.code = code; | ||
| 32 | } | ||
| 33 | |||
| 34 | pub inline fn isFinished(self: RangeDecoder) bool { | ||
| 35 | return self.code == 0; | ||
| 36 | } | ||
| 37 | |||
| 38 | inline fn normalize(self: *RangeDecoder, reader: anytype) !void { | ||
| 39 | if (self.range < 0x0100_0000) { | ||
| 40 | self.range <<= 8; | ||
| 41 | self.code = (self.code << 8) ^ @as(u32, try reader.readByte()); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | inline fn getBit(self: *RangeDecoder, reader: anytype) !bool { | ||
| 46 | self.range >>= 1; | ||
| 47 | |||
| 48 | const bit = self.code >= self.range; | ||
| 49 | if (bit) | ||
| 50 | self.code -= self.range; | ||
| 51 | |||
| 52 | try self.normalize(reader); | ||
| 53 | return bit; | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn get(self: *RangeDecoder, reader: anytype, count: usize) !u32 { | ||
| 57 | var result: u32 = 0; | ||
| 58 | var i: usize = 0; | ||
| 59 | while (i < count) : (i += 1) | ||
| 60 | result = (result << 1) ^ @boolToInt(try self.getBit(reader)); | ||
| 61 | return result; | ||
| 62 | } | ||
| 63 | |||
| 64 | pub inline fn decodeBit(self: *RangeDecoder, reader: anytype, prob: *u16, update: bool) !bool { | ||
| 65 | const bound = (self.range >> 11) * prob.*; | ||
| 66 | |||
| 67 | if (self.code < bound) { | ||
| 68 | if (update) | ||
| 69 | prob.* += (0x800 - prob.*) >> 5; | ||
| 70 | self.range = bound; | ||
| 71 | |||
| 72 | try self.normalize(reader); | ||
| 73 | return false; | ||
| 74 | } else { | ||
| 75 | if (update) | ||
| 76 | prob.* -= prob.* >> 5; | ||
| 77 | self.code -= bound; | ||
| 78 | self.range -= bound; | ||
| 79 | |||
| 80 | try self.normalize(reader); | ||
| 81 | return true; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | fn parseBitTree( | ||
| 86 | self: *RangeDecoder, | ||
| 87 | reader: anytype, | ||
| 88 | num_bits: u5, | ||
| 89 | probs: []u16, | ||
| 90 | update: bool, | ||
| 91 | ) !u32 { | ||
| 92 | var tmp: u32 = 1; | ||
| 93 | var i: @TypeOf(num_bits) = 0; | ||
| 94 | while (i < num_bits) : (i += 1) { | ||
| 95 | const bit = try self.decodeBit(reader, &probs[tmp], update); | ||
| 96 | tmp = (tmp << 1) ^ @boolToInt(bit); | ||
| 97 | } | ||
| 98 | return tmp - (@as(u32, 1) << num_bits); | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn parseReverseBitTree( | ||
| 102 | self: *RangeDecoder, | ||
| 103 | reader: anytype, | ||
| 104 | num_bits: u5, | ||
| 105 | probs: []u16, | ||
| 106 | offset: usize, | ||
| 107 | update: bool, | ||
| 108 | ) !u32 { | ||
| 109 | var result: u32 = 0; | ||
| 110 | var tmp: usize = 1; | ||
| 111 | var i: @TypeOf(num_bits) = 0; | ||
| 112 | while (i < num_bits) : (i += 1) { | ||
| 113 | const bit = @boolToInt(try self.decodeBit(reader, &probs[offset + tmp], update)); | ||
| 114 | tmp = (tmp << 1) ^ bit; | ||
| 115 | result ^= @as(u32, bit) << i; | ||
| 116 | } | ||
| 117 | return result; | ||
| 118 | } | ||
| 119 | }; | ||
| 120 | |||
| 121 | pub fn BitTree(comptime num_bits: usize) type { | ||
| 122 | return struct { | ||
| 123 | probs: [1 << num_bits]u16 = .{0x400} ** (1 << num_bits), | ||
| 124 | |||
| 125 | const Self = @This(); | ||
| 126 | |||
| 127 | pub fn parse( | ||
| 128 | self: *Self, | ||
| 129 | reader: anytype, | ||
| 130 | decoder: *RangeDecoder, | ||
| 131 | update: bool, | ||
| 132 | ) !u32 { | ||
| 133 | return decoder.parseBitTree(reader, num_bits, &self.probs, update); | ||
| 134 | } | ||
| 135 | |||
| 136 | pub fn parseReverse( | ||
| 137 | self: *Self, | ||
| 138 | reader: anytype, | ||
| 139 | decoder: *RangeDecoder, | ||
| 140 | update: bool, | ||
| 141 | ) !u32 { | ||
| 142 | return decoder.parseReverseBitTree(reader, num_bits, &self.probs, 0, update); | ||
| 143 | } | ||
| 144 | |||
| 145 | pub fn reset(self: *Self) void { | ||
| 146 | mem.set(u16, &self.probs, 0x400); | ||
| 147 | } | ||
| 148 | }; | ||
| 149 | } | ||
| 150 | |||
| 151 | pub const LenDecoder = struct { | ||
| 152 | choice: u16 = 0x400, | ||
| 153 | choice2: u16 = 0x400, | ||
| 154 | low_coder: [16]BitTree(3) = .{.{}} ** 16, | ||
| 155 | mid_coder: [16]BitTree(3) = .{.{}} ** 16, | ||
| 156 | high_coder: BitTree(8) = .{}, | ||
| 157 | |||
| 158 | pub fn decode( | ||
| 159 | self: *LenDecoder, | ||
| 160 | reader: anytype, | ||
| 161 | decoder: *RangeDecoder, | ||
| 162 | pos_state: usize, | ||
| 163 | update: bool, | ||
| 164 | ) !usize { | ||
| 165 | if (!try decoder.decodeBit(reader, &self.choice, update)) { | ||
| 166 | return @as(usize, try self.low_coder[pos_state].parse(reader, decoder, update)); | ||
| 167 | } else if (!try decoder.decodeBit(reader, &self.choice2, update)) { | ||
| 168 | return @as(usize, try self.mid_coder[pos_state].parse(reader, decoder, update)) + 8; | ||
| 169 | } else { | ||
| 170 | return @as(usize, try self.high_coder.parse(reader, decoder, update)) + 16; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | pub fn reset(self: *LenDecoder) void { | ||
| 175 | self.choice = 0x400; | ||
| 176 | self.choice2 = 0x400; | ||
| 177 | for (self.low_coder) |*t| t.reset(); | ||
| 178 | for (self.mid_coder) |*t| t.reset(); | ||
| 179 | self.high_coder.reset(); | ||
| 180 | } | ||
| 181 | }; | ||
lib/std/compress/lzma/test.zig created+89| ... | @@ -0,0 +1,89 @@ | ||
| 1 | const std = @import("../../std.zig"); | ||
| 2 | const lzma = @import("../lzma.zig"); | ||
| 3 | |||
| 4 | fn testDecompress(compressed: []const u8) ![]u8 { | ||
| 5 | const allocator = std.testing.allocator; | ||
| 6 | var stream = std.io.fixedBufferStream(compressed); | ||
| 7 | var decompressor = try lzma.decompress(allocator, stream.reader()); | ||
| 8 | defer decompressor.deinit(); | ||
| 9 | const reader = decompressor.reader(); | ||
| 10 | return reader.readAllAlloc(allocator, std.math.maxInt(usize)); | ||
| 11 | } | ||
| 12 | |||
| 13 | fn testDecompressEqual(expected: []const u8, compressed: []const u8) !void { | ||
| 14 | const allocator = std.testing.allocator; | ||
| 15 | const decomp = try testDecompress(compressed); | ||
| 16 | defer allocator.free(decomp); | ||
| 17 | try std.testing.expectEqualSlices(u8, expected, decomp); | ||
| 18 | } | ||
| 19 | |||
| 20 | fn testDecompressError(expected: anyerror, compressed: []const u8) !void { | ||
| 21 | return std.testing.expectError(expected, testDecompress(compressed)); | ||
| 22 | } | ||
| 23 | |||
| 24 | test "LZMA: decompress empty world" { | ||
| 25 | try testDecompressEqual( | ||
| 26 | "", | ||
| 27 | &[_]u8{ | ||
| 28 | 0x5d, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x83, 0xff, | ||
| 29 | 0xfb, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, | ||
| 30 | }, | ||
| 31 | ); | ||
| 32 | } | ||
| 33 | |||
| 34 | test "LZMA: decompress hello world" { | ||
| 35 | try testDecompressEqual( | ||
| 36 | "Hello world\n", | ||
| 37 | &[_]u8{ | ||
| 38 | 0x5d, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x24, 0x19, | ||
| 39 | 0x49, 0x98, 0x6f, 0x10, 0x19, 0xc6, 0xd7, 0x31, 0xeb, 0x36, 0x50, 0xb2, 0x98, 0x48, 0xff, 0xfe, | ||
| 40 | 0xa5, 0xb0, 0x00, | ||
| 41 | }, | ||
| 42 | ); | ||
| 43 | } | ||
| 44 | |||
| 45 | test "LZMA: decompress huge dict" { | ||
| 46 | try testDecompressEqual( | ||
| 47 | "Hello world\n", | ||
| 48 | &[_]u8{ | ||
| 49 | 0x5d, 0x7f, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x24, 0x19, | ||
| 50 | 0x49, 0x98, 0x6f, 0x10, 0x19, 0xc6, 0xd7, 0x31, 0xeb, 0x36, 0x50, 0xb2, 0x98, 0x48, 0xff, 0xfe, | ||
| 51 | 0xa5, 0xb0, 0x00, | ||
| 52 | }, | ||
| 53 | ); | ||
| 54 | } | ||
| 55 | |||
| 56 | test "LZMA: unknown size with end of payload marker" { | ||
| 57 | try testDecompressEqual( | ||
| 58 | "Hello\nWorld!\n", | ||
| 59 | @embedFile("testdata/good-unknown_size-with_eopm.lzma"), | ||
| 60 | ); | ||
| 61 | } | ||
| 62 | |||
| 63 | test "LZMA: known size without end of payload marker" { | ||
| 64 | try testDecompressEqual( | ||
| 65 | "Hello\nWorld!\n", | ||
| 66 | @embedFile("testdata/good-known_size-without_eopm.lzma"), | ||
| 67 | ); | ||
| 68 | } | ||
| 69 | |||
| 70 | test "LZMA: known size with end of payload marker" { | ||
| 71 | try testDecompressEqual( | ||
| 72 | "Hello\nWorld!\n", | ||
| 73 | @embedFile("testdata/good-known_size-with_eopm.lzma"), | ||
| 74 | ); | ||
| 75 | } | ||
| 76 | |||
| 77 | test "LZMA: too big uncompressed size in header" { | ||
| 78 | try testDecompressError( | ||
| 79 | error.CorruptInput, | ||
| 80 | @embedFile("testdata/bad-too_big_size-with_eopm.lzma"), | ||
| 81 | ); | ||
| 82 | } | ||
| 83 | |||
| 84 | test "LZMA: too small uncompressed size in header" { | ||
| 85 | try testDecompressError( | ||
| 86 | error.CorruptInput, | ||
| 87 | @embedFile("testdata/bad-too_small_size-without_eopm-3.lzma"), | ||
| 88 | ); | ||
| 89 | } | ||
lib/std/compress/lzma/testdata/bad-too_big_size-with_eopm.lzma created| Binary files /dev/null and b/lib/std/compress/lzma/testdata/bad-too_big_size-with_eopm.lzma differ | |||
lib/std/compress/lzma/testdata/bad-too_small_size-without_eopm-3.lzma created| Binary files /dev/null and b/lib/std/compress/lzma/testdata/bad-too_small_size-without_eopm-3.lzma differ | |||
lib/std/compress/lzma/testdata/good-known_size-with_eopm.lzma created| Binary files /dev/null and b/lib/std/compress/lzma/testdata/good-known_size-with_eopm.lzma differ | |||
lib/std/compress/lzma/testdata/good-known_size-without_eopm.lzma created| Binary files /dev/null and b/lib/std/compress/lzma/testdata/good-known_size-without_eopm.lzma differ | |||
lib/std/compress/lzma/testdata/good-unknown_size-with_eopm.lzma created| Binary files /dev/null and b/lib/std/compress/lzma/testdata/good-unknown_size-with_eopm.lzma differ | |||
lib/std/compress/lzma/vec2d.zig created+128| ... | @@ -0,0 +1,128 @@ | ||
| 1 | const std = @import("../../std.zig"); | ||
| 2 | const math = std.math; | ||
| 3 | const mem = std.mem; | ||
| 4 | const Allocator = std.mem.Allocator; | ||
| 5 | |||
| 6 | pub fn Vec2D(comptime T: type) type { | ||
| 7 | return struct { | ||
| 8 | data: []T, | ||
| 9 | cols: usize, | ||
| 10 | |||
| 11 | const Self = @This(); | ||
| 12 | |||
| 13 | pub fn init(allocator: Allocator, value: T, size: struct { usize, usize }) !Self { | ||
| 14 | const len = try math.mul(usize, size[0], size[1]); | ||
| 15 | const data = try allocator.alloc(T, len); | ||
| 16 | mem.set(T, data, value); | ||
| 17 | return Self{ | ||
| 18 | .data = data, | ||
| 19 | .cols = size[1], | ||
| 20 | }; | ||
| 21 | } | ||
| 22 | |||
| 23 | pub fn deinit(self: *Self, allocator: Allocator) void { | ||
| 24 | allocator.free(self.data); | ||
| 25 | self.* = undefined; | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn fill(self: *Self, value: T) void { | ||
| 29 | mem.set(T, self.data, value); | ||
| 30 | } | ||
| 31 | |||
| 32 | inline fn _get(self: Self, row: usize) ![]T { | ||
| 33 | const start_row = try math.mul(usize, row, self.cols); | ||
| 34 | const end_row = try math.add(usize, start_row, self.cols); | ||
| 35 | return self.data[start_row..end_row]; | ||
| 36 | } | ||
| 37 | |||
| 38 | pub fn get(self: Self, row: usize) ![]const T { | ||
| 39 | return self._get(row); | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn getMut(self: *Self, row: usize) ![]T { | ||
| 43 | return self._get(row); | ||
| 44 | } | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | |||
| 48 | const testing = std.testing; | ||
| 49 | const expectEqualSlices = std.testing.expectEqualSlices; | ||
| 50 | const expectError = std.testing.expectError; | ||
| 51 | |||
| 52 | test "Vec2D.init" { | ||
| 53 | const allocator = testing.allocator; | ||
| 54 | var vec2d = try Vec2D(i32).init(allocator, 1, .{ 2, 3 }); | ||
| 55 | defer vec2d.deinit(allocator); | ||
| 56 | |||
| 57 | try expectEqualSlices(i32, &.{ 1, 1, 1 }, try vec2d.get(0)); | ||
| 58 | try expectEqualSlices(i32, &.{ 1, 1, 1 }, try vec2d.get(1)); | ||
| 59 | } | ||
| 60 | |||
| 61 | test "Vec2D.init overflow" { | ||
| 62 | const allocator = testing.allocator; | ||
| 63 | try expectError( | ||
| 64 | error.Overflow, | ||
| 65 | Vec2D(i32).init(allocator, 1, .{ math.maxInt(usize), math.maxInt(usize) }), | ||
| 66 | ); | ||
| 67 | } | ||
| 68 | |||
| 69 | test "Vec2D.fill" { | ||
| 70 | const allocator = testing.allocator; | ||
| 71 | var vec2d = try Vec2D(i32).init(allocator, 0, .{ 2, 3 }); | ||
| 72 | defer vec2d.deinit(allocator); | ||
| 73 | |||
| 74 | vec2d.fill(7); | ||
| 75 | |||
| 76 | try expectEqualSlices(i32, &.{ 7, 7, 7 }, try vec2d.get(0)); | ||
| 77 | try expectEqualSlices(i32, &.{ 7, 7, 7 }, try vec2d.get(1)); | ||
| 78 | } | ||
| 79 | |||
| 80 | test "Vec2D.get" { | ||
| 81 | var data = [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 82 | const vec2d = Vec2D(i32){ | ||
| 83 | .data = &data, | ||
| 84 | .cols = 2, | ||
| 85 | }; | ||
| 86 | |||
| 87 | try expectEqualSlices(i32, &.{ 0, 1 }, try vec2d.get(0)); | ||
| 88 | try expectEqualSlices(i32, &.{ 2, 3 }, try vec2d.get(1)); | ||
| 89 | try expectEqualSlices(i32, &.{ 4, 5 }, try vec2d.get(2)); | ||
| 90 | try expectEqualSlices(i32, &.{ 6, 7 }, try vec2d.get(3)); | ||
| 91 | } | ||
| 92 | |||
| 93 | test "Vec2D.getMut" { | ||
| 94 | var data = [_]i32{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 95 | var vec2d = Vec2D(i32){ | ||
| 96 | .data = &data, | ||
| 97 | .cols = 2, | ||
| 98 | }; | ||
| 99 | |||
| 100 | const row = try vec2d.getMut(1); | ||
| 101 | row[1] = 9; | ||
| 102 | |||
| 103 | try expectEqualSlices(i32, &.{ 0, 1 }, try vec2d.get(0)); | ||
| 104 | // (1, 1) should be 9. | ||
| 105 | try expectEqualSlices(i32, &.{ 2, 9 }, try vec2d.get(1)); | ||
| 106 | try expectEqualSlices(i32, &.{ 4, 5 }, try vec2d.get(2)); | ||
| 107 | try expectEqualSlices(i32, &.{ 6, 7 }, try vec2d.get(3)); | ||
| 108 | } | ||
| 109 | |||
| 110 | test "Vec2D.get multiplication overflow" { | ||
| 111 | const allocator = testing.allocator; | ||
| 112 | var matrix = try Vec2D(i32).init(allocator, 0, .{ 3, 4 }); | ||
| 113 | defer matrix.deinit(allocator); | ||
| 114 | |||
| 115 | const row = (math.maxInt(usize) / 4) + 1; | ||
| 116 | try expectError(error.Overflow, matrix.get(row)); | ||
| 117 | try expectError(error.Overflow, matrix.getMut(row)); | ||
| 118 | } | ||
| 119 | |||
| 120 | test "Vec2D.get addition overflow" { | ||
| 121 | const allocator = testing.allocator; | ||
| 122 | var matrix = try Vec2D(i32).init(allocator, 0, .{ 3, 5 }); | ||
| 123 | defer matrix.deinit(allocator); | ||
| 124 | |||
| 125 | const row = math.maxInt(usize) / 5; | ||
| 126 | try expectError(error.Overflow, matrix.get(row)); | ||
| 127 | try expectError(error.Overflow, matrix.getMut(row)); | ||
| 128 | } | ||
lib/std/compress/lzma2.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const Allocator = std.mem.Allocator; | ||
| 3 | |||
| 4 | pub const decode = @import("lzma2/decode.zig"); | ||
| 5 | |||
| 6 | pub fn decompress( | ||
| 7 | allocator: Allocator, | ||
| 8 | reader: anytype, | ||
| 9 | writer: anytype, | ||
| 10 | ) !void { | ||
| 11 | var decoder = try decode.Decoder.init(allocator); | ||
| 12 | defer decoder.deinit(allocator); | ||
| 13 | return decoder.decompress(allocator, reader, writer); | ||
| 14 | } | ||
| 15 | |||
| 16 | test { | ||
| 17 | const expected = "Hello\nWorld!\n"; | ||
| 18 | const compressed = &[_]u8{ 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02, 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00 }; | ||
| 19 | |||
| 20 | const allocator = std.testing.allocator; | ||
| 21 | var decomp = std.ArrayList(u8).init(allocator); | ||
| 22 | defer decomp.deinit(); | ||
| 23 | var stream = std.io.fixedBufferStream(compressed); | ||
| 24 | try decompress(allocator, stream.reader(), decomp.writer()); | ||
| 25 | try std.testing.expectEqualSlices(u8, expected, decomp.items); | ||
| 26 | } | ||
lib/std/compress/lzma2/decode.zig created+169| ... | @@ -0,0 +1,169 @@ | ||
| 1 | const std = @import("../../std.zig"); | ||
| 2 | const Allocator = std.mem.Allocator; | ||
| 3 | |||
| 4 | const lzma = @import("../lzma.zig"); | ||
| 5 | const DecoderState = lzma.decode.DecoderState; | ||
| 6 | const LzAccumBuffer = lzma.decode.lzbuffer.LzAccumBuffer; | ||
| 7 | const Properties = lzma.decode.Properties; | ||
| 8 | const RangeDecoder = lzma.decode.rangecoder.RangeDecoder; | ||
| 9 | |||
| 10 | pub const Decoder = struct { | ||
| 11 | lzma_state: DecoderState, | ||
| 12 | |||
| 13 | pub fn init(allocator: Allocator) !Decoder { | ||
| 14 | return Decoder{ | ||
| 15 | .lzma_state = try DecoderState.init( | ||
| 16 | allocator, | ||
| 17 | Properties{ | ||
| 18 | .lc = 0, | ||
| 19 | .lp = 0, | ||
| 20 | .pb = 0, | ||
| 21 | }, | ||
| 22 | null, | ||
| 23 | ), | ||
| 24 | }; | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn deinit(self: *Decoder, allocator: Allocator) void { | ||
| 28 | self.lzma_state.deinit(allocator); | ||
| 29 | self.* = undefined; | ||
| 30 | } | ||
| 31 | |||
| 32 | pub fn decompress( | ||
| 33 | self: *Decoder, | ||
| 34 | allocator: Allocator, | ||
| 35 | reader: anytype, | ||
| 36 | writer: anytype, | ||
| 37 | ) !void { | ||
| 38 | var accum = LzAccumBuffer.init(std.math.maxInt(usize)); | ||
| 39 | defer accum.deinit(allocator); | ||
| 40 | |||
| 41 | while (true) { | ||
| 42 | const status = try reader.readByte(); | ||
| 43 | |||
| 44 | switch (status) { | ||
| 45 | 0 => break, | ||
| 46 | 1 => try parseUncompressed(allocator, reader, writer, &accum, true), | ||
| 47 | 2 => try parseUncompressed(allocator, reader, writer, &accum, false), | ||
| 48 | else => try self.parseLzma(allocator, reader, writer, &accum, status), | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | try accum.finish(writer); | ||
| 53 | } | ||
| 54 | |||
| 55 | fn parseLzma( | ||
| 56 | self: *Decoder, | ||
| 57 | allocator: Allocator, | ||
| 58 | reader: anytype, | ||
| 59 | writer: anytype, | ||
| 60 | accum: *LzAccumBuffer, | ||
| 61 | status: u8, | ||
| 62 | ) !void { | ||
| 63 | if (status & 0x80 == 0) { | ||
| 64 | return error.CorruptInput; | ||
| 65 | } | ||
| 66 | |||
| 67 | const Reset = struct { | ||
| 68 | dict: bool, | ||
| 69 | state: bool, | ||
| 70 | props: bool, | ||
| 71 | }; | ||
| 72 | |||
| 73 | const reset = switch ((status >> 5) & 0x3) { | ||
| 74 | 0 => Reset{ | ||
| 75 | .dict = false, | ||
| 76 | .state = false, | ||
| 77 | .props = false, | ||
| 78 | }, | ||
| 79 | 1 => Reset{ | ||
| 80 | .dict = false, | ||
| 81 | .state = true, | ||
| 82 | .props = false, | ||
| 83 | }, | ||
| 84 | 2 => Reset{ | ||
| 85 | .dict = false, | ||
| 86 | .state = true, | ||
| 87 | .props = true, | ||
| 88 | }, | ||
| 89 | 3 => Reset{ | ||
| 90 | .dict = true, | ||
| 91 | .state = true, | ||
| 92 | .props = true, | ||
| 93 | }, | ||
| 94 | else => unreachable, | ||
| 95 | }; | ||
| 96 | |||
| 97 | const unpacked_size = blk: { | ||
| 98 | var tmp: u64 = status & 0x1F; | ||
| 99 | tmp <<= 16; | ||
| 100 | tmp |= try reader.readIntBig(u16); | ||
| 101 | break :blk tmp + 1; | ||
| 102 | }; | ||
| 103 | |||
| 104 | const packed_size = blk: { | ||
| 105 | const tmp: u17 = try reader.readIntBig(u16); | ||
| 106 | break :blk tmp + 1; | ||
| 107 | }; | ||
| 108 | |||
| 109 | if (reset.dict) { | ||
| 110 | try accum.reset(writer); | ||
| 111 | } | ||
| 112 | |||
| 113 | if (reset.state) { | ||
| 114 | var new_props = self.lzma_state.lzma_props; | ||
| 115 | |||
| 116 | if (reset.props) { | ||
| 117 | var props = try reader.readByte(); | ||
| 118 | if (props >= 225) { | ||
| 119 | return error.CorruptInput; | ||
| 120 | } | ||
| 121 | |||
| 122 | const lc = @intCast(u4, props % 9); | ||
| 123 | props /= 9; | ||
| 124 | const lp = @intCast(u3, props % 5); | ||
| 125 | props /= 5; | ||
| 126 | const pb = @intCast(u3, props); | ||
| 127 | |||
| 128 | if (lc + lp > 4) { | ||
| 129 | return error.CorruptInput; | ||
| 130 | } | ||
| 131 | |||
| 132 | new_props = Properties{ .lc = lc, .lp = lp, .pb = pb }; | ||
| 133 | } | ||
| 134 | |||
| 135 | try self.lzma_state.resetState(allocator, new_props); | ||
| 136 | } | ||
| 137 | |||
| 138 | self.lzma_state.unpacked_size = unpacked_size + accum.len; | ||
| 139 | |||
| 140 | var counter = std.io.countingReader(reader); | ||
| 141 | const counter_reader = counter.reader(); | ||
| 142 | |||
| 143 | var rangecoder = try RangeDecoder.init(counter_reader); | ||
| 144 | while (try self.lzma_state.process(allocator, counter_reader, writer, accum, &rangecoder) == .continue_) {} | ||
| 145 | |||
| 146 | if (counter.bytes_read != packed_size) { | ||
| 147 | return error.CorruptInput; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | fn parseUncompressed( | ||
| 152 | allocator: Allocator, | ||
| 153 | reader: anytype, | ||
| 154 | writer: anytype, | ||
| 155 | accum: *LzAccumBuffer, | ||
| 156 | reset_dict: bool, | ||
| 157 | ) !void { | ||
| 158 | const unpacked_size = @as(u17, try reader.readIntBig(u16)) + 1; | ||
| 159 | |||
| 160 | if (reset_dict) { | ||
| 161 | try accum.reset(writer); | ||
| 162 | } | ||
| 163 | |||
| 164 | var i: @TypeOf(unpacked_size) = 0; | ||
| 165 | while (i < unpacked_size) : (i += 1) { | ||
| 166 | try accum.appendByte(allocator, try reader.readByte()); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | }; | ||
lib/std/compress/xz.zig+1-1| ... | @@ -118,7 +118,7 @@ pub fn Decompress(comptime ReaderType: type) type { | ... | @@ -118,7 +118,7 @@ pub fn Decompress(comptime ReaderType: type) type { |
| 118 | var hasher = std.compress.hashedReader(self.in_reader, Crc32.init()); | 118 | var hasher = std.compress.hashedReader(self.in_reader, Crc32.init()); |
| 119 | const hashed_reader = hasher.reader(); | 119 | const hashed_reader = hasher.reader(); |
| 120 | 120 | ||
| 121 | const backward_size = (try hashed_reader.readIntLittle(u32) + 1) * 4; | 121 | const backward_size = (@as(u64, try hashed_reader.readIntLittle(u32)) + 1) * 4; |
| 122 | if (backward_size != index_size) | 122 | if (backward_size != index_size) |
| 123 | return error.CorruptInput; | 123 | return error.CorruptInput; |
| 124 | 124 |
lib/std/compress/xz/block.zig+20-126| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("../../std.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const lzma = @import("lzma.zig"); | 2 | const lzma2 = std.compress.lzma2; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 4 | const Crc32 = std.hash.Crc32; | 5 | const Crc32 = std.hash.Crc32; |
| 5 | const Crc64 = std.hash.crc.Crc64Xz; | 6 | const Crc64 = std.hash.crc.Crc64Xz; |
| 6 | const Sha256 = std.crypto.hash.sha2.Sha256; | 7 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| ... | @@ -32,8 +33,7 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -32,8 +33,7 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 32 | inner_reader: ReaderType, | 33 | inner_reader: ReaderType, |
| 33 | check: xz.Check, | 34 | check: xz.Check, |
| 34 | err: ?Error, | 35 | err: ?Error, |
| 35 | accum: lzma.LzAccumBuffer, | 36 | to_read: ArrayListUnmanaged(u8), |
| 36 | lzma_state: lzma.DecoderState, | ||
| 37 | block_count: usize, | 37 | block_count: usize, |
| 38 | 38 | ||
| 39 | fn init(allocator: Allocator, in_reader: ReaderType, check: xz.Check) !Self { | 39 | fn init(allocator: Allocator, in_reader: ReaderType, check: xz.Check) !Self { |
| ... | @@ -42,15 +42,13 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -42,15 +42,13 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 42 | .inner_reader = in_reader, | 42 | .inner_reader = in_reader, |
| 43 | .check = check, | 43 | .check = check, |
| 44 | .err = null, | 44 | .err = null, |
| 45 | .accum = .{}, | 45 | .to_read = .{}, |
| 46 | .lzma_state = try lzma.DecoderState.init(allocator), | ||
| 47 | .block_count = 0, | 46 | .block_count = 0, |
| 48 | }; | 47 | }; |
| 49 | } | 48 | } |
| 50 | 49 | ||
| 51 | pub fn deinit(self: *Self) void { | 50 | pub fn deinit(self: *Self) void { |
| 52 | self.accum.deinit(self.allocator); | 51 | self.to_read.deinit(self.allocator); |
| 53 | self.lzma_state.deinit(self.allocator); | ||
| 54 | } | 52 | } |
| 55 | 53 | ||
| 56 | pub fn reader(self: *Self) Reader { | 54 | pub fn reader(self: *Self) Reader { |
| ... | @@ -59,9 +57,13 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -59,9 +57,13 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 59 | 57 | ||
| 60 | pub fn read(self: *Self, output: []u8) Error!usize { | 58 | pub fn read(self: *Self, output: []u8) Error!usize { |
| 61 | while (true) { | 59 | while (true) { |
| 62 | if (self.accum.to_read.items.len > 0) { | 60 | if (self.to_read.items.len > 0) { |
| 63 | const n = self.accum.read(output); | 61 | const input = self.to_read.items; |
| 64 | if (self.accum.to_read.items.len == 0 and self.err != null) { | 62 | const n = std.math.min(input.len, output.len); |
| 63 | std.mem.copy(u8, output[0..n], input[0..n]); | ||
| 64 | std.mem.copy(u8, input, input[n..]); | ||
| 65 | self.to_read.shrinkRetainingCapacity(input.len - n); | ||
| 66 | if (self.to_read.items.len == 0 and self.err != null) { | ||
| 65 | if (self.err.? == DecodeError.EndOfStreamWithNoError) { | 67 | if (self.err.? == DecodeError.EndOfStreamWithNoError) { |
| 66 | return n; | 68 | return n; |
| 67 | } | 69 | } |
| ... | @@ -77,15 +79,12 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -77,15 +79,12 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 77 | } | 79 | } |
| 78 | self.readBlock() catch |e| { | 80 | self.readBlock() catch |e| { |
| 79 | self.err = e; | 81 | self.err = e; |
| 80 | if (self.accum.to_read.items.len == 0) { | ||
| 81 | try self.accum.reset(self.allocator); | ||
| 82 | } | ||
| 83 | }; | 82 | }; |
| 84 | } | 83 | } |
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | fn readBlock(self: *Self) Error!void { | 86 | fn readBlock(self: *Self) Error!void { |
| 88 | const unpacked_pos = self.accum.to_read.items.len; | 87 | const unpacked_pos = self.to_read.items.len; |
| 89 | 88 | ||
| 90 | var block_counter = std.io.countingReader(self.inner_reader); | 89 | var block_counter = std.io.countingReader(self.inner_reader); |
| 91 | const block_reader = block_counter.reader(); | 90 | const block_reader = block_counter.reader(); |
| ... | @@ -98,7 +97,7 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -98,7 +97,7 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 98 | var header_hasher = std.compress.hashedReader(block_reader, Crc32.init()); | 97 | var header_hasher = std.compress.hashedReader(block_reader, Crc32.init()); |
| 99 | const header_reader = header_hasher.reader(); | 98 | const header_reader = header_hasher.reader(); |
| 100 | 99 | ||
| 101 | const header_size = try header_reader.readByte() * 4; | 100 | const header_size = @as(u64, try header_reader.readByte()) * 4; |
| 102 | if (header_size == 0) | 101 | if (header_size == 0) |
| 103 | return error.EndOfStreamWithNoError; | 102 | return error.EndOfStreamWithNoError; |
| 104 | 103 | ||
| ... | @@ -156,15 +155,18 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -156,15 +155,18 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 156 | 155 | ||
| 157 | // Compressed Data | 156 | // Compressed Data |
| 158 | var packed_counter = std.io.countingReader(block_reader); | 157 | var packed_counter = std.io.countingReader(block_reader); |
| 159 | const packed_reader = packed_counter.reader(); | 158 | try lzma2.decompress( |
| 160 | while (try self.readLzma2Chunk(packed_reader)) {} | 159 | self.allocator, |
| 160 | packed_counter.reader(), | ||
| 161 | self.to_read.writer(self.allocator), | ||
| 162 | ); | ||
| 161 | 163 | ||
| 162 | if (packed_size) |s| { | 164 | if (packed_size) |s| { |
| 163 | if (s != packed_counter.bytes_read) | 165 | if (s != packed_counter.bytes_read) |
| 164 | return error.CorruptInput; | 166 | return error.CorruptInput; |
| 165 | } | 167 | } |
| 166 | 168 | ||
| 167 | const unpacked_bytes = self.accum.to_read.items[unpacked_pos..]; | 169 | const unpacked_bytes = self.to_read.items[unpacked_pos..]; |
| 168 | if (unpacked_size) |s| { | 170 | if (unpacked_size) |s| { |
| 169 | if (s != unpacked_bytes.len) | 171 | if (s != unpacked_bytes.len) |
| 170 | return error.CorruptInput; | 172 | return error.CorruptInput; |
| ... | @@ -205,113 +207,5 @@ pub fn Decoder(comptime ReaderType: type) type { | ... | @@ -205,113 +207,5 @@ pub fn Decoder(comptime ReaderType: type) type { |
| 205 | 207 | ||
| 206 | self.block_count += 1; | 208 | self.block_count += 1; |
| 207 | } | 209 | } |
| 208 | |||
| 209 | fn readLzma2Chunk(self: *Self, packed_reader: anytype) Error!bool { | ||
| 210 | const status = try packed_reader.readByte(); | ||
| 211 | switch (status) { | ||
| 212 | 0 => { | ||
| 213 | try self.accum.reset(self.allocator); | ||
| 214 | return false; | ||
| 215 | }, | ||
| 216 | 1, 2 => { | ||
| 217 | if (status == 1) | ||
| 218 | try self.accum.reset(self.allocator); | ||
| 219 | |||
| 220 | const size = try packed_reader.readIntBig(u16) + 1; | ||
| 221 | try self.accum.ensureUnusedCapacity(self.allocator, size); | ||
| 222 | |||
| 223 | var i: usize = 0; | ||
| 224 | while (i < size) : (i += 1) | ||
| 225 | self.accum.appendAssumeCapacity(try packed_reader.readByte()); | ||
| 226 | |||
| 227 | return true; | ||
| 228 | }, | ||
| 229 | else => { | ||
| 230 | if (status & 0x80 == 0) | ||
| 231 | return error.CorruptInput; | ||
| 232 | |||
| 233 | const Reset = struct { | ||
| 234 | dict: bool, | ||
| 235 | state: bool, | ||
| 236 | props: bool, | ||
| 237 | }; | ||
| 238 | |||
| 239 | const reset = switch ((status >> 5) & 0x3) { | ||
| 240 | 0 => Reset{ | ||
| 241 | .dict = false, | ||
| 242 | .state = false, | ||
| 243 | .props = false, | ||
| 244 | }, | ||
| 245 | 1 => Reset{ | ||
| 246 | .dict = false, | ||
| 247 | .state = true, | ||
| 248 | .props = false, | ||
| 249 | }, | ||
| 250 | 2 => Reset{ | ||
| 251 | .dict = false, | ||
| 252 | .state = true, | ||
| 253 | .props = true, | ||
| 254 | }, | ||
| 255 | 3 => Reset{ | ||
| 256 | .dict = true, | ||
| 257 | .state = true, | ||
| 258 | .props = true, | ||
| 259 | }, | ||
| 260 | else => unreachable, | ||
| 261 | }; | ||
| 262 | |||
| 263 | const unpacked_size = blk: { | ||
| 264 | var tmp: u64 = status & 0x1F; | ||
| 265 | tmp <<= 16; | ||
| 266 | tmp |= try packed_reader.readIntBig(u16); | ||
| 267 | break :blk tmp + 1; | ||
| 268 | }; | ||
| 269 | |||
| 270 | const packed_size = blk: { | ||
| 271 | const tmp: u17 = try packed_reader.readIntBig(u16); | ||
| 272 | break :blk tmp + 1; | ||
| 273 | }; | ||
| 274 | |||
| 275 | if (reset.dict) | ||
| 276 | try self.accum.reset(self.allocator); | ||
| 277 | |||
| 278 | if (reset.state) { | ||
| 279 | var new_props = self.lzma_state.lzma_props; | ||
| 280 | |||
| 281 | if (reset.props) { | ||
| 282 | var props = try packed_reader.readByte(); | ||
| 283 | if (props >= 225) | ||
| 284 | return error.CorruptInput; | ||
| 285 | |||
| 286 | const lc = @intCast(u4, props % 9); | ||
| 287 | props /= 9; | ||
| 288 | const lp = @intCast(u3, props % 5); | ||
| 289 | props /= 5; | ||
| 290 | const pb = @intCast(u3, props); | ||
| 291 | |||
| 292 | if (lc + lp > 4) | ||
| 293 | return error.CorruptInput; | ||
| 294 | |||
| 295 | new_props = .{ .lc = lc, .lp = lp, .pb = pb }; | ||
| 296 | } | ||
| 297 | |||
| 298 | try self.lzma_state.reset_state(self.allocator, new_props); | ||
| 299 | } | ||
| 300 | |||
| 301 | self.lzma_state.unpacked_size = unpacked_size + self.accum.len(); | ||
| 302 | |||
| 303 | const buffer = try self.allocator.alloc(u8, packed_size); | ||
| 304 | defer self.allocator.free(buffer); | ||
| 305 | |||
| 306 | for (buffer) |*b| | ||
| 307 | b.* = try packed_reader.readByte(); | ||
| 308 | |||
| 309 | var rangecoder = try lzma.RangeDecoder.init(buffer); | ||
| 310 | try self.lzma_state.process(self.allocator, &self.accum, &rangecoder); | ||
| 311 | |||
| 312 | return true; | ||
| 313 | }, | ||
| 314 | } | ||
| 315 | } | ||
| 316 | }; | 210 | }; |
| 317 | } | 211 | } |
lib/std/compress/xz/lzma.zig deleted-658| ... | @@ -1,658 +0,0 @@ | ||
| 1 | // Ported from https://github.com/gendx/lzma-rs | ||
| 2 | |||
| 3 | const std = @import("../../std.zig"); | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const Allocator = std.mem.Allocator; | ||
| 6 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | ||
| 7 | |||
| 8 | const LzmaProperties = struct { | ||
| 9 | lc: u4, | ||
| 10 | lp: u3, | ||
| 11 | pb: u3, | ||
| 12 | |||
| 13 | fn validate(self: LzmaProperties) void { | ||
| 14 | assert(self.lc <= 8); | ||
| 15 | assert(self.lp <= 4); | ||
| 16 | assert(self.pb <= 4); | ||
| 17 | } | ||
| 18 | }; | ||
| 19 | |||
| 20 | pub const DecoderState = struct { | ||
| 21 | lzma_props: LzmaProperties, | ||
| 22 | unpacked_size: ?u64, | ||
| 23 | literal_probs: Vec2D(u16), | ||
| 24 | pos_slot_decoder: [4]BitTree, | ||
| 25 | align_decoder: BitTree, | ||
| 26 | pos_decoders: [115]u16, | ||
| 27 | is_match: [192]u16, | ||
| 28 | is_rep: [12]u16, | ||
| 29 | is_rep_g0: [12]u16, | ||
| 30 | is_rep_g1: [12]u16, | ||
| 31 | is_rep_g2: [12]u16, | ||
| 32 | is_rep_0long: [192]u16, | ||
| 33 | state: usize, | ||
| 34 | rep: [4]usize, | ||
| 35 | len_decoder: LenDecoder, | ||
| 36 | rep_len_decoder: LenDecoder, | ||
| 37 | |||
| 38 | pub fn init(allocator: Allocator) !DecoderState { | ||
| 39 | return .{ | ||
| 40 | .lzma_props = LzmaProperties{ .lc = 0, .lp = 0, .pb = 0 }, | ||
| 41 | .unpacked_size = null, | ||
| 42 | .literal_probs = try Vec2D(u16).init(allocator, 0x400, 1, 0x300), | ||
| 43 | .pos_slot_decoder = .{ | ||
| 44 | try BitTree.init(allocator, 6), | ||
| 45 | try BitTree.init(allocator, 6), | ||
| 46 | try BitTree.init(allocator, 6), | ||
| 47 | try BitTree.init(allocator, 6), | ||
| 48 | }, | ||
| 49 | .align_decoder = try BitTree.init(allocator, 4), | ||
| 50 | .pos_decoders = .{0x400} ** 115, | ||
| 51 | .is_match = .{0x400} ** 192, | ||
| 52 | .is_rep = .{0x400} ** 12, | ||
| 53 | .is_rep_g0 = .{0x400} ** 12, | ||
| 54 | .is_rep_g1 = .{0x400} ** 12, | ||
| 55 | .is_rep_g2 = .{0x400} ** 12, | ||
| 56 | .is_rep_0long = .{0x400} ** 192, | ||
| 57 | .state = 0, | ||
| 58 | .rep = .{0} ** 4, | ||
| 59 | .len_decoder = try LenDecoder.init(allocator), | ||
| 60 | .rep_len_decoder = try LenDecoder.init(allocator), | ||
| 61 | }; | ||
| 62 | } | ||
| 63 | |||
| 64 | pub fn deinit(self: *DecoderState, allocator: Allocator) void { | ||
| 65 | self.literal_probs.deinit(allocator); | ||
| 66 | for (self.pos_slot_decoder) |*t| t.deinit(allocator); | ||
| 67 | self.align_decoder.deinit(allocator); | ||
| 68 | self.len_decoder.deinit(allocator); | ||
| 69 | self.rep_len_decoder.deinit(allocator); | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn reset_state(self: *DecoderState, allocator: Allocator, new_props: LzmaProperties) !void { | ||
| 73 | new_props.validate(); | ||
| 74 | if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) { | ||
| 75 | self.literal_probs.fill(0x400); | ||
| 76 | } else { | ||
| 77 | self.literal_probs.deinit(allocator); | ||
| 78 | self.literal_probs = try Vec2D(u16).init(allocator, 0x400, @as(usize, 1) << (new_props.lc + new_props.lp), 0x300); | ||
| 79 | } | ||
| 80 | |||
| 81 | self.lzma_props = new_props; | ||
| 82 | for (self.pos_slot_decoder) |*t| t.reset(); | ||
| 83 | self.align_decoder.reset(); | ||
| 84 | self.pos_decoders = .{0x400} ** 115; | ||
| 85 | self.is_match = .{0x400} ** 192; | ||
| 86 | self.is_rep = .{0x400} ** 12; | ||
| 87 | self.is_rep_g0 = .{0x400} ** 12; | ||
| 88 | self.is_rep_g1 = .{0x400} ** 12; | ||
| 89 | self.is_rep_g2 = .{0x400} ** 12; | ||
| 90 | self.is_rep_0long = .{0x400} ** 192; | ||
| 91 | self.state = 0; | ||
| 92 | self.rep = .{0} ** 4; | ||
| 93 | self.len_decoder.reset(); | ||
| 94 | self.rep_len_decoder.reset(); | ||
| 95 | } | ||
| 96 | |||
| 97 | fn processNextInner( | ||
| 98 | self: *DecoderState, | ||
| 99 | allocator: Allocator, | ||
| 100 | output: *LzAccumBuffer, | ||
| 101 | rangecoder: *RangeDecoder, | ||
| 102 | update: bool, | ||
| 103 | ) !ProcessingStatus { | ||
| 104 | const pos_state = output.len() & ((@as(usize, 1) << self.lzma_props.pb) - 1); | ||
| 105 | |||
| 106 | if (!try rangecoder.decodeBit( | ||
| 107 | &self.is_match[(self.state << 4) + pos_state], | ||
| 108 | update, | ||
| 109 | )) { | ||
| 110 | const byte: u8 = try self.decodeLiteral(output, rangecoder, update); | ||
| 111 | |||
| 112 | if (update) { | ||
| 113 | try output.appendLiteral(allocator, byte); | ||
| 114 | |||
| 115 | self.state = if (self.state < 4) | ||
| 116 | 0 | ||
| 117 | else if (self.state < 10) | ||
| 118 | self.state - 3 | ||
| 119 | else | ||
| 120 | self.state - 6; | ||
| 121 | } | ||
| 122 | return .continue_; | ||
| 123 | } | ||
| 124 | |||
| 125 | var len: usize = undefined; | ||
| 126 | if (try rangecoder.decodeBit(&self.is_rep[self.state], update)) { | ||
| 127 | if (!try rangecoder.decodeBit(&self.is_rep_g0[self.state], update)) { | ||
| 128 | if (!try rangecoder.decodeBit( | ||
| 129 | &self.is_rep_0long[(self.state << 4) + pos_state], | ||
| 130 | update, | ||
| 131 | )) { | ||
| 132 | if (update) { | ||
| 133 | self.state = if (self.state < 7) 9 else 11; | ||
| 134 | const dist = self.rep[0] + 1; | ||
| 135 | try output.appendLz(allocator, 1, dist); | ||
| 136 | } | ||
| 137 | return .continue_; | ||
| 138 | } | ||
| 139 | } else { | ||
| 140 | const idx: usize = if (!try rangecoder.decodeBit(&self.is_rep_g1[self.state], update)) | ||
| 141 | 1 | ||
| 142 | else if (!try rangecoder.decodeBit(&self.is_rep_g2[self.state], update)) | ||
| 143 | 2 | ||
| 144 | else | ||
| 145 | 3; | ||
| 146 | if (update) { | ||
| 147 | const dist = self.rep[idx]; | ||
| 148 | var i = idx; | ||
| 149 | while (i > 0) : (i -= 1) { | ||
| 150 | self.rep[i] = self.rep[i - 1]; | ||
| 151 | } | ||
| 152 | self.rep[0] = dist; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | len = try self.rep_len_decoder.decode(rangecoder, pos_state, update); | ||
| 157 | |||
| 158 | if (update) { | ||
| 159 | self.state = if (self.state < 7) 8 else 11; | ||
| 160 | } | ||
| 161 | } else { | ||
| 162 | if (update) { | ||
| 163 | self.rep[3] = self.rep[2]; | ||
| 164 | self.rep[2] = self.rep[1]; | ||
| 165 | self.rep[1] = self.rep[0]; | ||
| 166 | } | ||
| 167 | |||
| 168 | len = try self.len_decoder.decode(rangecoder, pos_state, update); | ||
| 169 | |||
| 170 | if (update) { | ||
| 171 | self.state = if (self.state < 7) 7 else 10; | ||
| 172 | } | ||
| 173 | |||
| 174 | const rep_0 = try self.decodeDistance(rangecoder, len, update); | ||
| 175 | |||
| 176 | if (update) { | ||
| 177 | self.rep[0] = rep_0; | ||
| 178 | if (self.rep[0] == 0xFFFF_FFFF) { | ||
| 179 | if (rangecoder.isFinished()) { | ||
| 180 | return .finished; | ||
| 181 | } | ||
| 182 | return error.CorruptInput; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | if (update) { | ||
| 188 | len += 2; | ||
| 189 | |||
| 190 | const dist = self.rep[0] + 1; | ||
| 191 | try output.appendLz(allocator, len, dist); | ||
| 192 | } | ||
| 193 | |||
| 194 | return .continue_; | ||
| 195 | } | ||
| 196 | |||
| 197 | fn processNext( | ||
| 198 | self: *DecoderState, | ||
| 199 | allocator: Allocator, | ||
| 200 | output: *LzAccumBuffer, | ||
| 201 | rangecoder: *RangeDecoder, | ||
| 202 | ) !ProcessingStatus { | ||
| 203 | return self.processNextInner(allocator, output, rangecoder, true); | ||
| 204 | } | ||
| 205 | |||
| 206 | pub fn process( | ||
| 207 | self: *DecoderState, | ||
| 208 | allocator: Allocator, | ||
| 209 | output: *LzAccumBuffer, | ||
| 210 | rangecoder: *RangeDecoder, | ||
| 211 | ) !void { | ||
| 212 | while (true) { | ||
| 213 | if (self.unpacked_size) |unpacked_size| { | ||
| 214 | if (output.len() >= unpacked_size) { | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } else if (rangecoder.isFinished()) { | ||
| 218 | break; | ||
| 219 | } | ||
| 220 | |||
| 221 | if (try self.processNext(allocator, output, rangecoder) == .finished) { | ||
| 222 | break; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | if (self.unpacked_size) |len| { | ||
| 227 | if (len != output.len()) { | ||
| 228 | return error.CorruptInput; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | fn decodeLiteral( | ||
| 234 | self: *DecoderState, | ||
| 235 | output: *LzAccumBuffer, | ||
| 236 | rangecoder: *RangeDecoder, | ||
| 237 | update: bool, | ||
| 238 | ) !u8 { | ||
| 239 | const def_prev_byte = 0; | ||
| 240 | const prev_byte = @as(usize, output.lastOr(def_prev_byte)); | ||
| 241 | |||
| 242 | var result: usize = 1; | ||
| 243 | const lit_state = ((output.len() & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) + | ||
| 244 | (prev_byte >> (8 - self.lzma_props.lc)); | ||
| 245 | const probs = try self.literal_probs.get(lit_state); | ||
| 246 | |||
| 247 | if (self.state >= 7) { | ||
| 248 | var match_byte = @as(usize, try output.lastN(self.rep[0] + 1)); | ||
| 249 | |||
| 250 | while (result < 0x100) { | ||
| 251 | const match_bit = (match_byte >> 7) & 1; | ||
| 252 | match_byte <<= 1; | ||
| 253 | const bit = @boolToInt(try rangecoder.decodeBit( | ||
| 254 | &probs[((@as(usize, 1) + match_bit) << 8) + result], | ||
| 255 | update, | ||
| 256 | )); | ||
| 257 | result = (result << 1) ^ bit; | ||
| 258 | if (match_bit != bit) { | ||
| 259 | break; | ||
| 260 | } | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | while (result < 0x100) { | ||
| 265 | result = (result << 1) ^ @boolToInt(try rangecoder.decodeBit(&probs[result], update)); | ||
| 266 | } | ||
| 267 | |||
| 268 | return @truncate(u8, result - 0x100); | ||
| 269 | } | ||
| 270 | |||
| 271 | fn decodeDistance( | ||
| 272 | self: *DecoderState, | ||
| 273 | rangecoder: *RangeDecoder, | ||
| 274 | length: usize, | ||
| 275 | update: bool, | ||
| 276 | ) !usize { | ||
| 277 | const len_state = if (length > 3) 3 else length; | ||
| 278 | |||
| 279 | const pos_slot = @as(usize, try self.pos_slot_decoder[len_state].parse(rangecoder, update)); | ||
| 280 | if (pos_slot < 4) | ||
| 281 | return pos_slot; | ||
| 282 | |||
| 283 | const num_direct_bits = @intCast(u5, (pos_slot >> 1) - 1); | ||
| 284 | var result = (2 ^ (pos_slot & 1)) << num_direct_bits; | ||
| 285 | |||
| 286 | if (pos_slot < 14) { | ||
| 287 | result += try rangecoder.parseReverseBitTree( | ||
| 288 | num_direct_bits, | ||
| 289 | &self.pos_decoders, | ||
| 290 | result - pos_slot, | ||
| 291 | update, | ||
| 292 | ); | ||
| 293 | } else { | ||
| 294 | result += @as(usize, try rangecoder.get(num_direct_bits - 4)) << 4; | ||
| 295 | result += try self.align_decoder.parseReverse(rangecoder, update); | ||
| 296 | } | ||
| 297 | |||
| 298 | return result; | ||
| 299 | } | ||
| 300 | }; | ||
| 301 | |||
| 302 | const ProcessingStatus = enum { | ||
| 303 | continue_, | ||
| 304 | finished, | ||
| 305 | }; | ||
| 306 | |||
| 307 | pub const LzAccumBuffer = struct { | ||
| 308 | to_read: ArrayListUnmanaged(u8) = .{}, | ||
| 309 | buf: ArrayListUnmanaged(u8) = .{}, | ||
| 310 | |||
| 311 | pub fn deinit(self: *LzAccumBuffer, allocator: Allocator) void { | ||
| 312 | self.to_read.deinit(allocator); | ||
| 313 | self.buf.deinit(allocator); | ||
| 314 | } | ||
| 315 | |||
| 316 | pub fn read(self: *LzAccumBuffer, output: []u8) usize { | ||
| 317 | const input = self.to_read.items; | ||
| 318 | const n = std.math.min(input.len, output.len); | ||
| 319 | std.mem.copy(u8, output[0..n], input[0..n]); | ||
| 320 | std.mem.copy(u8, input, input[n..]); | ||
| 321 | self.to_read.shrinkRetainingCapacity(input.len - n); | ||
| 322 | return n; | ||
| 323 | } | ||
| 324 | |||
| 325 | pub fn ensureUnusedCapacity( | ||
| 326 | self: *LzAccumBuffer, | ||
| 327 | allocator: Allocator, | ||
| 328 | additional_count: usize, | ||
| 329 | ) !void { | ||
| 330 | try self.buf.ensureUnusedCapacity(allocator, additional_count); | ||
| 331 | } | ||
| 332 | |||
| 333 | pub fn appendAssumeCapacity(self: *LzAccumBuffer, byte: u8) void { | ||
| 334 | self.buf.appendAssumeCapacity(byte); | ||
| 335 | } | ||
| 336 | |||
| 337 | pub fn reset(self: *LzAccumBuffer, allocator: Allocator) !void { | ||
| 338 | try self.to_read.appendSlice(allocator, self.buf.items); | ||
| 339 | self.buf.clearRetainingCapacity(); | ||
| 340 | } | ||
| 341 | |||
| 342 | pub fn len(self: *const LzAccumBuffer) usize { | ||
| 343 | return self.buf.items.len; | ||
| 344 | } | ||
| 345 | |||
| 346 | pub fn lastOr(self: *const LzAccumBuffer, lit: u8) u8 { | ||
| 347 | const buf_len = self.buf.items.len; | ||
| 348 | return if (buf_len == 0) | ||
| 349 | lit | ||
| 350 | else | ||
| 351 | self.buf.items[buf_len - 1]; | ||
| 352 | } | ||
| 353 | |||
| 354 | pub fn lastN(self: *const LzAccumBuffer, dist: usize) !u8 { | ||
| 355 | const buf_len = self.buf.items.len; | ||
| 356 | if (dist > buf_len) { | ||
| 357 | return error.CorruptInput; | ||
| 358 | } | ||
| 359 | |||
| 360 | return self.buf.items[buf_len - dist]; | ||
| 361 | } | ||
| 362 | |||
| 363 | pub fn appendLiteral(self: *LzAccumBuffer, allocator: Allocator, lit: u8) !void { | ||
| 364 | try self.buf.append(allocator, lit); | ||
| 365 | } | ||
| 366 | |||
| 367 | pub fn appendLz(self: *LzAccumBuffer, allocator: Allocator, length: usize, dist: usize) !void { | ||
| 368 | const buf_len = self.buf.items.len; | ||
| 369 | if (dist > buf_len) { | ||
| 370 | return error.CorruptInput; | ||
| 371 | } | ||
| 372 | |||
| 373 | var offset = buf_len - dist; | ||
| 374 | var i: usize = 0; | ||
| 375 | while (i < length) : (i += 1) { | ||
| 376 | const x = self.buf.items[offset]; | ||
| 377 | try self.buf.append(allocator, x); | ||
| 378 | offset += 1; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | }; | ||
| 382 | |||
| 383 | pub const RangeDecoder = struct { | ||
| 384 | stream: std.io.FixedBufferStream([]const u8), | ||
| 385 | range: u32, | ||
| 386 | code: u32, | ||
| 387 | |||
| 388 | pub fn init(buffer: []const u8) !RangeDecoder { | ||
| 389 | var dec = RangeDecoder{ | ||
| 390 | .stream = std.io.fixedBufferStream(buffer), | ||
| 391 | .range = 0xFFFF_FFFF, | ||
| 392 | .code = 0, | ||
| 393 | }; | ||
| 394 | const reader = dec.stream.reader(); | ||
| 395 | _ = try reader.readByte(); | ||
| 396 | dec.code = try reader.readIntBig(u32); | ||
| 397 | return dec; | ||
| 398 | } | ||
| 399 | |||
| 400 | pub fn fromParts( | ||
| 401 | buffer: []const u8, | ||
| 402 | range: u32, | ||
| 403 | code: u32, | ||
| 404 | ) RangeDecoder { | ||
| 405 | return .{ | ||
| 406 | .stream = std.io.fixedBufferStream(buffer), | ||
| 407 | .range = range, | ||
| 408 | .code = code, | ||
| 409 | }; | ||
| 410 | } | ||
| 411 | |||
| 412 | pub fn set(self: *RangeDecoder, range: u32, code: u32) void { | ||
| 413 | self.range = range; | ||
| 414 | self.code = code; | ||
| 415 | } | ||
| 416 | |||
| 417 | pub fn readInto(self: *RangeDecoder, dest: []u8) !usize { | ||
| 418 | return self.stream.read(dest); | ||
| 419 | } | ||
| 420 | |||
| 421 | pub inline fn isFinished(self: *const RangeDecoder) bool { | ||
| 422 | return self.code == 0 and self.isEof(); | ||
| 423 | } | ||
| 424 | |||
| 425 | pub inline fn isEof(self: *const RangeDecoder) bool { | ||
| 426 | return self.stream.pos == self.stream.buffer.len; | ||
| 427 | } | ||
| 428 | |||
| 429 | inline fn normalize(self: *RangeDecoder) !void { | ||
| 430 | if (self.range < 0x0100_0000) { | ||
| 431 | self.range <<= 8; | ||
| 432 | self.code = (self.code << 8) ^ @as(u32, try self.stream.reader().readByte()); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | inline fn getBit(self: *RangeDecoder) !bool { | ||
| 437 | self.range >>= 1; | ||
| 438 | |||
| 439 | const bit = self.code >= self.range; | ||
| 440 | if (bit) | ||
| 441 | self.code -= self.range; | ||
| 442 | |||
| 443 | try self.normalize(); | ||
| 444 | return bit; | ||
| 445 | } | ||
| 446 | |||
| 447 | fn get(self: *RangeDecoder, count: usize) !u32 { | ||
| 448 | var result: u32 = 0; | ||
| 449 | var i: usize = 0; | ||
| 450 | while (i < count) : (i += 1) | ||
| 451 | result = (result << 1) ^ @boolToInt(try self.getBit()); | ||
| 452 | return result; | ||
| 453 | } | ||
| 454 | |||
| 455 | pub inline fn decodeBit(self: *RangeDecoder, prob: *u16, update: bool) !bool { | ||
| 456 | const bound = (self.range >> 11) * prob.*; | ||
| 457 | |||
| 458 | if (self.code < bound) { | ||
| 459 | if (update) | ||
| 460 | prob.* += (0x800 - prob.*) >> 5; | ||
| 461 | self.range = bound; | ||
| 462 | |||
| 463 | try self.normalize(); | ||
| 464 | return false; | ||
| 465 | } else { | ||
| 466 | if (update) | ||
| 467 | prob.* -= prob.* >> 5; | ||
| 468 | self.code -= bound; | ||
| 469 | self.range -= bound; | ||
| 470 | |||
| 471 | try self.normalize(); | ||
| 472 | return true; | ||
| 473 | } | ||
| 474 | } | ||
| 475 | |||
| 476 | fn parseBitTree( | ||
| 477 | self: *RangeDecoder, | ||
| 478 | num_bits: u5, | ||
| 479 | probs: []u16, | ||
| 480 | update: bool, | ||
| 481 | ) !u32 { | ||
| 482 | var tmp: u32 = 1; | ||
| 483 | var i: u5 = 0; | ||
| 484 | while (i < num_bits) : (i += 1) { | ||
| 485 | const bit = try self.decodeBit(&probs[tmp], update); | ||
| 486 | tmp = (tmp << 1) ^ @boolToInt(bit); | ||
| 487 | } | ||
| 488 | return tmp - (@as(u32, 1) << num_bits); | ||
| 489 | } | ||
| 490 | |||
| 491 | pub fn parseReverseBitTree( | ||
| 492 | self: *RangeDecoder, | ||
| 493 | num_bits: u5, | ||
| 494 | probs: []u16, | ||
| 495 | offset: usize, | ||
| 496 | update: bool, | ||
| 497 | ) !u32 { | ||
| 498 | var result: u32 = 0; | ||
| 499 | var tmp: usize = 1; | ||
| 500 | var i: u5 = 0; | ||
| 501 | while (i < num_bits) : (i += 1) { | ||
| 502 | const bit = @boolToInt(try self.decodeBit(&probs[offset + tmp], update)); | ||
| 503 | tmp = (tmp << 1) ^ bit; | ||
| 504 | result ^= @as(u32, bit) << i; | ||
| 505 | } | ||
| 506 | return result; | ||
| 507 | } | ||
| 508 | }; | ||
| 509 | |||
| 510 | fn Vec2D(comptime T: type) type { | ||
| 511 | return struct { | ||
| 512 | data: []T, | ||
| 513 | cols: usize, | ||
| 514 | |||
| 515 | const Self = @This(); | ||
| 516 | |||
| 517 | pub fn init(allocator: Allocator, data: T, rows: usize, cols: usize) !Self { | ||
| 518 | const len = try std.math.mul(usize, rows, cols); | ||
| 519 | var vec2d = Self{ | ||
| 520 | .data = try allocator.alloc(T, len), | ||
| 521 | .cols = cols, | ||
| 522 | }; | ||
| 523 | vec2d.fill(data); | ||
| 524 | return vec2d; | ||
| 525 | } | ||
| 526 | |||
| 527 | pub fn deinit(self: *Self, allocator: Allocator) void { | ||
| 528 | allocator.free(self.data); | ||
| 529 | } | ||
| 530 | |||
| 531 | pub fn fill(self: *Self, value: T) void { | ||
| 532 | std.mem.set(T, self.data, value); | ||
| 533 | } | ||
| 534 | |||
| 535 | pub fn get(self: *Self, row: usize) ![]T { | ||
| 536 | const start_row = try std.math.mul(usize, row, self.cols); | ||
| 537 | return self.data[start_row .. start_row + self.cols]; | ||
| 538 | } | ||
| 539 | }; | ||
| 540 | } | ||
| 541 | |||
| 542 | const BitTree = struct { | ||
| 543 | num_bits: u5, | ||
| 544 | probs: ArrayListUnmanaged(u16), | ||
| 545 | |||
| 546 | pub fn init(allocator: Allocator, num_bits: u5) !BitTree { | ||
| 547 | var probs_len = @as(usize, 1) << num_bits; | ||
| 548 | var probs = try ArrayListUnmanaged(u16).initCapacity(allocator, probs_len); | ||
| 549 | while (probs_len > 0) : (probs_len -= 1) | ||
| 550 | probs.appendAssumeCapacity(0x400); | ||
| 551 | return .{ .num_bits = num_bits, .probs = probs }; | ||
| 552 | } | ||
| 553 | |||
| 554 | pub fn deinit(self: *BitTree, allocator: Allocator) void { | ||
| 555 | self.probs.deinit(allocator); | ||
| 556 | } | ||
| 557 | |||
| 558 | pub fn parse( | ||
| 559 | self: *BitTree, | ||
| 560 | rangecoder: *RangeDecoder, | ||
| 561 | update: bool, | ||
| 562 | ) !u32 { | ||
| 563 | return rangecoder.parseBitTree(self.num_bits, self.probs.items, update); | ||
| 564 | } | ||
| 565 | |||
| 566 | pub fn parseReverse( | ||
| 567 | self: *BitTree, | ||
| 568 | rangecoder: *RangeDecoder, | ||
| 569 | update: bool, | ||
| 570 | ) !u32 { | ||
| 571 | return rangecoder.parseReverseBitTree(self.num_bits, self.probs.items, 0, update); | ||
| 572 | } | ||
| 573 | |||
| 574 | pub fn reset(self: *BitTree) void { | ||
| 575 | std.mem.set(u16, self.probs.items, 0x400); | ||
| 576 | } | ||
| 577 | }; | ||
| 578 | |||
| 579 | const LenDecoder = struct { | ||
| 580 | choice: u16, | ||
| 581 | choice2: u16, | ||
| 582 | low_coder: [16]BitTree, | ||
| 583 | mid_coder: [16]BitTree, | ||
| 584 | high_coder: BitTree, | ||
| 585 | |||
| 586 | pub fn init(allocator: Allocator) !LenDecoder { | ||
| 587 | return .{ | ||
| 588 | .choice = 0x400, | ||
| 589 | .choice2 = 0x400, | ||
| 590 | .low_coder = .{ | ||
| 591 | try BitTree.init(allocator, 3), | ||
| 592 | try BitTree.init(allocator, 3), | ||
| 593 | try BitTree.init(allocator, 3), | ||
| 594 | try BitTree.init(allocator, 3), | ||
| 595 | try BitTree.init(allocator, 3), | ||
| 596 | try BitTree.init(allocator, 3), | ||
| 597 | try BitTree.init(allocator, 3), | ||
| 598 | try BitTree.init(allocator, 3), | ||
| 599 | try BitTree.init(allocator, 3), | ||
| 600 | try BitTree.init(allocator, 3), | ||
| 601 | try BitTree.init(allocator, 3), | ||
| 602 | try BitTree.init(allocator, 3), | ||
| 603 | try BitTree.init(allocator, 3), | ||
| 604 | try BitTree.init(allocator, 3), | ||
| 605 | try BitTree.init(allocator, 3), | ||
| 606 | try BitTree.init(allocator, 3), | ||
| 607 | }, | ||
| 608 | .mid_coder = .{ | ||
| 609 | try BitTree.init(allocator, 3), | ||
| 610 | try BitTree.init(allocator, 3), | ||
| 611 | try BitTree.init(allocator, 3), | ||
| 612 | try BitTree.init(allocator, 3), | ||
| 613 | try BitTree.init(allocator, 3), | ||
| 614 | try BitTree.init(allocator, 3), | ||
| 615 | try BitTree.init(allocator, 3), | ||
| 616 | try BitTree.init(allocator, 3), | ||
| 617 | try BitTree.init(allocator, 3), | ||
| 618 | try BitTree.init(allocator, 3), | ||
| 619 | try BitTree.init(allocator, 3), | ||
| 620 | try BitTree.init(allocator, 3), | ||
| 621 | try BitTree.init(allocator, 3), | ||
| 622 | try BitTree.init(allocator, 3), | ||
| 623 | try BitTree.init(allocator, 3), | ||
| 624 | try BitTree.init(allocator, 3), | ||
| 625 | }, | ||
| 626 | .high_coder = try BitTree.init(allocator, 8), | ||
| 627 | }; | ||
| 628 | } | ||
| 629 | |||
| 630 | pub fn deinit(self: *LenDecoder, allocator: Allocator) void { | ||
| 631 | for (self.low_coder) |*t| t.deinit(allocator); | ||
| 632 | for (self.mid_coder) |*t| t.deinit(allocator); | ||
| 633 | self.high_coder.deinit(allocator); | ||
| 634 | } | ||
| 635 | |||
| 636 | pub fn decode( | ||
| 637 | self: *LenDecoder, | ||
| 638 | rangecoder: *RangeDecoder, | ||
| 639 | pos_state: usize, | ||
| 640 | update: bool, | ||
| 641 | ) !usize { | ||
| 642 | if (!try rangecoder.decodeBit(&self.choice, update)) { | ||
| 643 | return @as(usize, try self.low_coder[pos_state].parse(rangecoder, update)); | ||
| 644 | } else if (!try rangecoder.decodeBit(&self.choice2, update)) { | ||
| 645 | return @as(usize, try self.mid_coder[pos_state].parse(rangecoder, update)) + 8; | ||
| 646 | } else { | ||
| 647 | return @as(usize, try self.high_coder.parse(rangecoder, update)) + 16; | ||
| 648 | } | ||
| 649 | } | ||
| 650 | |||
| 651 | pub fn reset(self: *LenDecoder) void { | ||
| 652 | self.choice = 0x400; | ||
| 653 | self.choice2 = 0x400; | ||
| 654 | for (self.low_coder) |*t| t.reset(); | ||
| 655 | for (self.mid_coder) |*t| t.reset(); | ||
| 656 | self.high_coder.reset(); | ||
| 657 | } | ||
| 658 | }; | ||
lib/std/compress/xz/test.zig+20| ... | @@ -78,3 +78,23 @@ test "unsupported" { | ... | @@ -78,3 +78,23 @@ test "unsupported" { |
| 78 | ); | 78 | ); |
| 79 | } | 79 | } |
| 80 | } | 80 | } |
| 81 | |||
| 82 | fn testDontPanic(data: []const u8) !void { | ||
| 83 | const buf = decompress(data) catch |err| switch (err) { | ||
| 84 | error.OutOfMemory => |e| return e, | ||
| 85 | else => return, | ||
| 86 | }; | ||
| 87 | defer testing.allocator.free(buf); | ||
| 88 | } | ||
| 89 | |||
| 90 | test "size fields: integer overflow avoidance" { | ||
| 91 | // These cases were found via fuzz testing and each previously caused | ||
| 92 | // an integer overflow when decoding. We just want to ensure they no longer | ||
| 93 | // cause a panic | ||
| 94 | const header_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6z"; | ||
| 95 | try testDontPanic(header_size_overflow); | ||
| 96 | const lzma2_chunk_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6\x02\x00!\x01\x08\x00\x00\x00\xd8\x0f#\x13\x01\xff\xff"; | ||
| 97 | try testDontPanic(lzma2_chunk_size_overflow); | ||
| 98 | const backward_size_overflow = "\xfd7zXZ\x00\x00\x01i\"\xde6\x00\x00\x00\x00\x1c\xdfD!\x90B\x99\r\x01\x00\x00\xff\xff\x10\x00\x00\x00\x01DD\xff\xff\xff\x01"; | ||
| 99 | try testDontPanic(backward_size_overflow); | ||
| 100 | } |
lib/std/crypto.zig+5| ... | @@ -41,11 +41,13 @@ pub const auth = struct { | ... | @@ -41,11 +41,13 @@ pub const auth = struct { |
| 41 | pub const Aegis128LMac = @import("crypto/aegis.zig").Aegis128LMac; | 41 | pub const Aegis128LMac = @import("crypto/aegis.zig").Aegis128LMac; |
| 42 | pub const Aegis256Mac = @import("crypto/aegis.zig").Aegis256Mac; | 42 | pub const Aegis256Mac = @import("crypto/aegis.zig").Aegis256Mac; |
| 43 | }; | 43 | }; |
| 44 | pub const cmac = @import("crypto/cmac.zig"); | ||
| 44 | }; | 45 | }; |
| 45 | 46 | ||
| 46 | /// Core functions, that should rarely be used directly by applications. | 47 | /// Core functions, that should rarely be used directly by applications. |
| 47 | pub const core = struct { | 48 | pub const core = struct { |
| 48 | pub const aes = @import("crypto/aes.zig"); | 49 | pub const aes = @import("crypto/aes.zig"); |
| 50 | pub const Ascon = @import("crypto/ascon.zig").State; | ||
| 49 | pub const Gimli = @import("crypto/gimli.zig").State; | 51 | pub const Gimli = @import("crypto/gimli.zig").State; |
| 50 | pub const Xoodoo = @import("crypto/xoodoo.zig").State; | 52 | pub const Xoodoo = @import("crypto/xoodoo.zig").State; |
| 51 | 53 | ||
| ... | @@ -202,10 +204,13 @@ test { | ... | @@ -202,10 +204,13 @@ test { |
| 202 | _ = aead.salsa_poly.XSalsa20Poly1305; | 204 | _ = aead.salsa_poly.XSalsa20Poly1305; |
| 203 | 205 | ||
| 204 | _ = auth.hmac; | 206 | _ = auth.hmac; |
| 207 | _ = auth.cmac; | ||
| 205 | _ = auth.siphash; | 208 | _ = auth.siphash; |
| 206 | 209 | ||
| 207 | _ = core.aes; | 210 | _ = core.aes; |
| 211 | _ = core.Ascon; | ||
| 208 | _ = core.Gimli; | 212 | _ = core.Gimli; |
| 213 | _ = core.Xoodoo; | ||
| 209 | _ = core.modes; | 214 | _ = core.modes; |
| 210 | 215 | ||
| 211 | _ = dh.X25519; | 216 | _ = dh.X25519; |
lib/std/crypto/ascon.zig created+227| ... | @@ -0,0 +1,227 @@ | ||
| 1 | //! Ascon is a 320-bit permutation, selected as new standard for lightweight cryptography | ||
| 2 | //! in the NIST Lightweight Cryptography competition (2019–2023). | ||
| 3 | //! https://csrc.nist.gov/News/2023/lightweight-cryptography-nist-selects-ascon | ||
| 4 | //! | ||
| 5 | //! The permutation is compact, and optimized for timing and side channel resistance, | ||
| 6 | //! making it a good choice for embedded applications. | ||
| 7 | //! | ||
| 8 | //! It is not meant to be used directly, but as a building block for symmetric cryptography. | ||
| 9 | |||
| 10 | const std = @import("std"); | ||
| 11 | const builtin = std.builtin; | ||
| 12 | const debug = std.debug; | ||
| 13 | const mem = std.mem; | ||
| 14 | const testing = std.testing; | ||
| 15 | const rotr = std.math.rotr; | ||
| 16 | |||
| 17 | /// An Ascon state. | ||
| 18 | /// | ||
| 19 | /// The state is represented as 5 64-bit words. | ||
| 20 | /// | ||
| 21 | /// The NIST submission (v1.2) serializes these words as big-endian, | ||
| 22 | /// but software implementations are free to use native endianness. | ||
| 23 | pub fn State(comptime endian: builtin.Endian) type { | ||
| 24 | return struct { | ||
| 25 | const Self = @This(); | ||
| 26 | |||
| 27 | /// Number of bytes in the state. | ||
| 28 | pub const block_bytes = 40; | ||
| 29 | |||
| 30 | const Block = [5]u64; | ||
| 31 | |||
| 32 | st: Block, | ||
| 33 | |||
| 34 | /// Initialize the state from a slice of bytes. | ||
| 35 | pub fn init(initial_state: [block_bytes]u8) Self { | ||
| 36 | var state = Self{ .st = undefined }; | ||
| 37 | mem.copy(u8, state.asBytes(), &initial_state); | ||
| 38 | state.endianSwap(); | ||
| 39 | return state; | ||
| 40 | } | ||
| 41 | |||
| 42 | /// Initialize the state from u64 words in native endianness. | ||
| 43 | pub fn initFromWords(initial_state: [5]u64) Self { | ||
| 44 | var state = Self{ .st = initial_state }; | ||
| 45 | return state; | ||
| 46 | } | ||
| 47 | |||
| 48 | /// Initialize the state for Ascon XOF | ||
| 49 | pub fn initXof() Self { | ||
| 50 | return Self{ .st = Block{ | ||
| 51 | 0xb57e273b814cd416, | ||
| 52 | 0x2b51042562ae2420, | ||
| 53 | 0x66a3a7768ddf2218, | ||
| 54 | 0x5aad0a7a8153650c, | ||
| 55 | 0x4f3e0e32539493b6, | ||
| 56 | } }; | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Initialize the state for Ascon XOFa | ||
| 60 | pub fn initXofA() Self { | ||
| 61 | return Self{ .st = Block{ | ||
| 62 | 0x44906568b77b9832, | ||
| 63 | 0xcd8d6cae53455532, | ||
| 64 | 0xf7b5212756422129, | ||
| 65 | 0x246885e1de0d225b, | ||
| 66 | 0xa8cb5ce33449973f, | ||
| 67 | } }; | ||
| 68 | } | ||
| 69 | |||
| 70 | /// A representation of the state as bytes. The byte order is architecture-dependent. | ||
| 71 | pub fn asBytes(self: *Self) *[block_bytes]u8 { | ||
| 72 | return mem.asBytes(&self.st); | ||
| 73 | } | ||
| 74 | |||
| 75 | /// Byte-swap the entire state if the architecture doesn't match the required endianness. | ||
| 76 | pub fn endianSwap(self: *Self) void { | ||
| 77 | for (self.st) |*w| { | ||
| 78 | w.* = mem.toNative(u64, w.*, endian); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | /// Set bytes starting at the beginning of the state. | ||
| 83 | pub fn setBytes(self: *Self, bytes: []const u8) void { | ||
| 84 | var i: usize = 0; | ||
| 85 | while (i + 8 <= bytes.len) : (i += 8) { | ||
| 86 | self.st[i / 8] = mem.readInt(u64, bytes[i..][0..8], endian); | ||
| 87 | } | ||
| 88 | if (i < bytes.len) { | ||
| 89 | var padded = [_]u8{0} ** 8; | ||
| 90 | mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]); | ||
| 91 | self.st[i / 8] = mem.readInt(u64, padded[0..], endian); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// XOR a byte into the state at a given offset. | ||
| 96 | pub fn addByte(self: *Self, byte: u8, offset: usize) void { | ||
| 97 | const z = switch (endian) { | ||
| 98 | .Big => 64 - 8 - 8 * @truncate(u6, offset % 8), | ||
| 99 | .Little => 8 * @truncate(u6, offset % 8), | ||
| 100 | }; | ||
| 101 | self.st[offset / 8] ^= @as(u64, byte) << z; | ||
| 102 | } | ||
| 103 | |||
| 104 | /// XOR bytes into the beginning of the state. | ||
| 105 | pub fn addBytes(self: *Self, bytes: []const u8) void { | ||
| 106 | var i: usize = 0; | ||
| 107 | while (i + 8 <= bytes.len) : (i += 8) { | ||
| 108 | self.st[i / 8] ^= mem.readInt(u64, bytes[i..][0..8], endian); | ||
| 109 | } | ||
| 110 | if (i < bytes.len) { | ||
| 111 | var padded = [_]u8{0} ** 8; | ||
| 112 | mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]); | ||
| 113 | self.st[i / 8] ^= mem.readInt(u64, padded[0..], endian); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Extract the first bytes of the state. | ||
| 118 | pub fn extractBytes(self: *Self, out: []u8) void { | ||
| 119 | var i: usize = 0; | ||
| 120 | while (i + 8 <= out.len) : (i += 8) { | ||
| 121 | mem.writeInt(u64, out[i..][0..8], self.st[i / 8], endian); | ||
| 122 | } | ||
| 123 | if (i < out.len) { | ||
| 124 | var padded = [_]u8{0} ** 8; | ||
| 125 | mem.writeInt(u64, padded[0..], self.st[i / 8], endian); | ||
| 126 | mem.copy(u8, out[i..], padded[0 .. out.len - i]); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | /// XOR the first bytes of the state into a slice of bytes. | ||
| 131 | pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void { | ||
| 132 | debug.assert(out.len == in.len); | ||
| 133 | |||
| 134 | var i: usize = 0; | ||
| 135 | while (i + 8 <= in.len) : (i += 8) { | ||
| 136 | const x = mem.readIntNative(u64, in[i..][0..8]) ^ mem.nativeTo(u64, self.st[i / 8], endian); | ||
| 137 | mem.writeIntNative(u64, out[i..][0..8], x); | ||
| 138 | } | ||
| 139 | if (i < in.len) { | ||
| 140 | var padded = [_]u8{0} ** 8; | ||
| 141 | mem.copy(u8, padded[0 .. in.len - i], in[i..]); | ||
| 142 | const x = mem.readIntNative(u64, &padded) ^ mem.nativeTo(u64, self.st[i / 8], endian); | ||
| 143 | mem.writeIntNative(u64, &padded, x); | ||
| 144 | mem.copy(u8, out[i..], padded[0 .. in.len - i]); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Set the words storing the bytes of a given range to zero. | ||
| 149 | pub fn clear(self: *Self, from: usize, to: usize) void { | ||
| 150 | mem.set(u64, self.st[from / 8 .. (to + 7) / 8], 0); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Clear the entire state, disabling compiler optimizations. | ||
| 154 | pub fn secureZero(self: *Self) void { | ||
| 155 | std.crypto.utils.secureZero(u64, &self.st); | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Apply a reduced-round permutation to the state. | ||
| 159 | pub inline fn permuteR(state: *Self, comptime rounds: u4) void { | ||
| 160 | const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b }; | ||
| 161 | inline for (rks[rks.len - rounds ..]) |rk| { | ||
| 162 | state.round(rk); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Apply a full-round permutation to the state. | ||
| 167 | pub inline fn permute(state: *Self) void { | ||
| 168 | state.permuteR(12); | ||
| 169 | } | ||
| 170 | |||
| 171 | // Core Ascon permutation. | ||
| 172 | inline fn round(state: *Self, rk: u64) void { | ||
| 173 | const x = &state.st; | ||
| 174 | x[2] ^= rk; | ||
| 175 | |||
| 176 | x[0] ^= x[4]; | ||
| 177 | x[4] ^= x[3]; | ||
| 178 | x[2] ^= x[1]; | ||
| 179 | var t: Block = .{ | ||
| 180 | x[0] ^ (~x[1] & x[2]), | ||
| 181 | x[1] ^ (~x[2] & x[3]), | ||
| 182 | x[2] ^ (~x[3] & x[4]), | ||
| 183 | x[3] ^ (~x[4] & x[0]), | ||
| 184 | x[4] ^ (~x[0] & x[1]), | ||
| 185 | }; | ||
| 186 | t[1] ^= t[0]; | ||
| 187 | t[3] ^= t[2]; | ||
| 188 | t[0] ^= t[4]; | ||
| 189 | |||
| 190 | x[2] = t[2] ^ rotr(u64, t[2], 6 - 1); | ||
| 191 | x[3] = t[3] ^ rotr(u64, t[3], 17 - 10); | ||
| 192 | x[4] = t[4] ^ rotr(u64, t[4], 41 - 7); | ||
| 193 | x[0] = t[0] ^ rotr(u64, t[0], 28 - 19); | ||
| 194 | x[1] = t[1] ^ rotr(u64, t[1], 61 - 39); | ||
| 195 | x[2] = t[2] ^ rotr(u64, x[2], 1); | ||
| 196 | x[3] = t[3] ^ rotr(u64, x[3], 10); | ||
| 197 | x[4] = t[4] ^ rotr(u64, x[4], 7); | ||
| 198 | x[0] = t[0] ^ rotr(u64, x[0], 19); | ||
| 199 | x[1] = t[1] ^ rotr(u64, x[1], 39); | ||
| 200 | x[2] = ~x[2]; | ||
| 201 | } | ||
| 202 | }; | ||
| 203 | } | ||
| 204 | |||
| 205 | test "ascon" { | ||
| 206 | const Ascon = State(.Big); | ||
| 207 | const bytes = [_]u8{0x01} ** Ascon.block_bytes; | ||
| 208 | var st = Ascon.init(bytes); | ||
| 209 | var out: [Ascon.block_bytes]u8 = undefined; | ||
| 210 | st.permute(); | ||
| 211 | st.extractBytes(&out); | ||
| 212 | const expected1 = [_]u8{ 148, 147, 49, 226, 218, 221, 208, 113, 186, 94, 96, 10, 183, 219, 119, 150, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 }; | ||
| 213 | try testing.expectEqualSlices(u8, &expected1, &out); | ||
| 214 | st.clear(0, 10); | ||
| 215 | st.extractBytes(&out); | ||
| 216 | const expected2 = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 }; | ||
| 217 | try testing.expectEqualSlices(u8, &expected2, &out); | ||
| 218 | st.addByte(1, 5); | ||
| 219 | st.addByte(2, 5); | ||
| 220 | st.extractBytes(&out); | ||
| 221 | const expected3 = [_]u8{ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 }; | ||
| 222 | try testing.expectEqualSlices(u8, &expected3, &out); | ||
| 223 | st.addBytes(&bytes); | ||
| 224 | st.extractBytes(&out); | ||
| 225 | const expected4 = [_]u8{ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 168, 207, 64, 19, 214, 96, 79, 107, 119, 80, 210, 151, 53, 16, 116, 65, 217, 44, 149, 241, 64, 180, 91, 181 }; | ||
| 226 | try testing.expectEqualSlices(u8, &expected4, &out); | ||
| 227 | } | ||
lib/std/crypto/benchmark.zig+1| ... | @@ -66,6 +66,7 @@ const macs = [_]Crypto{ | ... | @@ -66,6 +66,7 @@ const macs = [_]Crypto{ |
| 66 | Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" }, | 66 | Crypto{ .ty = crypto.auth.siphash.SipHash128(1, 3), .name = "siphash128-1-3" }, |
| 67 | Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" }, | 67 | Crypto{ .ty = crypto.auth.aegis.Aegis128LMac, .name = "aegis-128l mac" }, |
| 68 | Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" }, | 68 | Crypto{ .ty = crypto.auth.aegis.Aegis256Mac, .name = "aegis-256 mac" }, |
| 69 | Crypto{ .ty = crypto.auth.cmac.CmacAes128, .name = "aes-cmac" }, | ||
| 69 | }; | 70 | }; |
| 70 | 71 | ||
| 71 | pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 { | 72 | pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 { |
lib/std/crypto/cmac.zig created+156| ... | @@ -0,0 +1,156 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const crypto = std.crypto; | ||
| 3 | const mem = std.mem; | ||
| 4 | |||
| 5 | /// CMAC with AES-128 - RFC 4493 https://www.rfc-editor.org/rfc/rfc4493 | ||
| 6 | pub const CmacAes128 = Cmac(crypto.core.aes.Aes128); | ||
| 7 | |||
| 8 | /// NIST Special Publication 800-38B - The CMAC Mode for Authentication | ||
| 9 | /// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf | ||
| 10 | pub fn Cmac(comptime BlockCipher: type) type { | ||
| 11 | const BlockCipherCtx = @typeInfo(@TypeOf(BlockCipher.initEnc)).Fn.return_type.?; | ||
| 12 | const Block = [BlockCipher.block.block_length]u8; | ||
| 13 | |||
| 14 | return struct { | ||
| 15 | const Self = @This(); | ||
| 16 | pub const key_length = BlockCipher.key_bits / 8; | ||
| 17 | pub const block_length = BlockCipher.block.block_length; | ||
| 18 | pub const mac_length = block_length; | ||
| 19 | |||
| 20 | cipher_ctx: BlockCipherCtx, | ||
| 21 | k1: Block, | ||
| 22 | k2: Block, | ||
| 23 | buf: Block = [_]u8{0} ** block_length, | ||
| 24 | pos: usize = 0, | ||
| 25 | |||
| 26 | pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [key_length]u8) void { | ||
| 27 | var ctx = Self.init(key); | ||
| 28 | ctx.update(msg); | ||
| 29 | ctx.final(out); | ||
| 30 | } | ||
| 31 | |||
| 32 | pub fn init(key: *const [key_length]u8) Self { | ||
| 33 | const cipher_ctx = BlockCipher.initEnc(key.*); | ||
| 34 | const zeros = [_]u8{0} ** block_length; | ||
| 35 | var k1: Block = undefined; | ||
| 36 | cipher_ctx.encrypt(&k1, &zeros); | ||
| 37 | k1 = double(k1); | ||
| 38 | return Self{ | ||
| 39 | .cipher_ctx = cipher_ctx, | ||
| 40 | .k1 = k1, | ||
| 41 | .k2 = double(k1), | ||
| 42 | }; | ||
| 43 | } | ||
| 44 | |||
| 45 | pub fn update(self: *Self, msg: []const u8) void { | ||
| 46 | const left = block_length - self.pos; | ||
| 47 | var m = msg; | ||
| 48 | if (m.len > left) { | ||
| 49 | for (self.buf[self.pos..]) |*b, i| b.* ^= m[i]; | ||
| 50 | m = m[left..]; | ||
| 51 | self.cipher_ctx.encrypt(&self.buf, &self.buf); | ||
| 52 | self.pos = 0; | ||
| 53 | } | ||
| 54 | while (m.len > block_length) { | ||
| 55 | for (self.buf[0..block_length]) |*b, i| b.* ^= m[i]; | ||
| 56 | m = m[block_length..]; | ||
| 57 | self.cipher_ctx.encrypt(&self.buf, &self.buf); | ||
| 58 | self.pos = 0; | ||
| 59 | } | ||
| 60 | if (m.len > 0) { | ||
| 61 | for (self.buf[self.pos..][0..m.len]) |*b, i| b.* ^= m[i]; | ||
| 62 | self.pos += m.len; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn final(self: *Self, out: *[mac_length]u8) void { | ||
| 67 | var mac = self.k1; | ||
| 68 | if (self.pos < block_length) { | ||
| 69 | mac = self.k2; | ||
| 70 | mac[self.pos] ^= 0x80; | ||
| 71 | } | ||
| 72 | for (mac) |*b, i| b.* ^= self.buf[i]; | ||
| 73 | self.cipher_ctx.encrypt(out, &mac); | ||
| 74 | } | ||
| 75 | |||
| 76 | fn double(l: Block) Block { | ||
| 77 | const Int = std.meta.Int(.unsigned, block_length * 8); | ||
| 78 | const l_ = mem.readIntBig(Int, &l); | ||
| 79 | const l_2 = switch (block_length) { | ||
| 80 | 8 => (l_ << 1) ^ (0x1b & -%(l_ >> 63)), // mod x^64 + x^4 + x^3 + x + 1 | ||
| 81 | 16 => (l_ << 1) ^ (0x87 & -%(l_ >> 127)), // mod x^128 + x^7 + x^2 + x + 1 | ||
| 82 | 32 => (l_ << 1) ^ (0x0425 & -%(l_ >> 255)), // mod x^256 + x^10 + x^5 + x^2 + 1 | ||
| 83 | 64 => (l_ << 1) ^ (0x0125 & -%(l_ >> 511)), // mod x^512 + x^8 + x^5 + x^2 + 1 | ||
| 84 | else => @compileError("unsupported block length"), | ||
| 85 | }; | ||
| 86 | var l2: Block = undefined; | ||
| 87 | mem.writeIntBig(Int, &l2, l_2); | ||
| 88 | return l2; | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | const testing = std.testing; | ||
| 94 | |||
| 95 | test "CmacAes128 - Example 1: len = 0" { | ||
| 96 | const key = [_]u8{ | ||
| 97 | 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, | ||
| 98 | }; | ||
| 99 | var msg: [0]u8 = undefined; | ||
| 100 | const exp = [_]u8{ | ||
| 101 | 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46, | ||
| 102 | }; | ||
| 103 | var out: [CmacAes128.mac_length]u8 = undefined; | ||
| 104 | CmacAes128.create(&out, &msg, &key); | ||
| 105 | try testing.expectEqualSlices(u8, &out, &exp); | ||
| 106 | } | ||
| 107 | |||
| 108 | test "CmacAes128 - Example 2: len = 16" { | ||
| 109 | const key = [_]u8{ | ||
| 110 | 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, | ||
| 111 | }; | ||
| 112 | const msg = [_]u8{ | ||
| 113 | 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, | ||
| 114 | }; | ||
| 115 | const exp = [_]u8{ | ||
| 116 | 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c, | ||
| 117 | }; | ||
| 118 | var out: [CmacAes128.mac_length]u8 = undefined; | ||
| 119 | CmacAes128.create(&out, &msg, &key); | ||
| 120 | try testing.expectEqualSlices(u8, &out, &exp); | ||
| 121 | } | ||
| 122 | |||
| 123 | test "CmacAes128 - Example 3: len = 40" { | ||
| 124 | const key = [_]u8{ | ||
| 125 | 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, | ||
| 126 | }; | ||
| 127 | const msg = [_]u8{ | ||
| 128 | 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, | ||
| 129 | 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, | ||
| 130 | 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, | ||
| 131 | }; | ||
| 132 | const exp = [_]u8{ | ||
| 133 | 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27, | ||
| 134 | }; | ||
| 135 | var out: [CmacAes128.mac_length]u8 = undefined; | ||
| 136 | CmacAes128.create(&out, &msg, &key); | ||
| 137 | try testing.expectEqualSlices(u8, &out, &exp); | ||
| 138 | } | ||
| 139 | |||
| 140 | test "CmacAes128 - Example 4: len = 64" { | ||
| 141 | const key = [_]u8{ | ||
| 142 | 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, | ||
| 143 | }; | ||
| 144 | const msg = [_]u8{ | ||
| 145 | 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, | ||
| 146 | 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, | ||
| 147 | 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, | ||
| 148 | 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, | ||
| 149 | }; | ||
| 150 | const exp = [_]u8{ | ||
| 151 | 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe, | ||
| 152 | }; | ||
| 153 | var out: [CmacAes128.mac_length]u8 = undefined; | ||
| 154 | CmacAes128.create(&out, &msg, &key); | ||
| 155 | try testing.expectEqualSlices(u8, &out, &exp); | ||
| 156 | } | ||
lib/std/crypto/isap.zig+40-114| ... | @@ -1,9 +1,11 @@ | ... | @@ -1,9 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const crypto = std.crypto; | ||
| 2 | const debug = std.debug; | 3 | const debug = std.debug; |
| 3 | const mem = std.mem; | 4 | const mem = std.mem; |
| 4 | const math = std.math; | 5 | const math = std.math; |
| 5 | const testing = std.testing; | 6 | const testing = std.testing; |
| 6 | const AuthenticationError = std.crypto.errors.AuthenticationError; | 7 | const Ascon = crypto.core.Ascon(.Big); |
| 8 | const AuthenticationError = crypto.errors.AuthenticationError; | ||
| 7 | 9 | ||
| 8 | /// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks. | 10 | /// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks. |
| 9 | /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf | 11 | /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf |
| ... | @@ -25,90 +27,26 @@ pub const IsapA128A = struct { | ... | @@ -25,90 +27,26 @@ pub const IsapA128A = struct { |
| 25 | const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c }; | 27 | const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c }; |
| 26 | const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c }; | 28 | const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c }; |
| 27 | 29 | ||
| 28 | const Block = [5]u64; | 30 | st: Ascon, |
| 29 | |||
| 30 | block: Block, | ||
| 31 | |||
| 32 | fn round(isap: *IsapA128A, rk: u64) void { | ||
| 33 | var x = &isap.block; | ||
| 34 | x[2] ^= rk; | ||
| 35 | x[0] ^= x[4]; | ||
| 36 | x[4] ^= x[3]; | ||
| 37 | x[2] ^= x[1]; | ||
| 38 | var t = x.*; | ||
| 39 | x[0] = t[0] ^ ((~t[1]) & t[2]); | ||
| 40 | x[2] = t[2] ^ ((~t[3]) & t[4]); | ||
| 41 | x[4] = t[4] ^ ((~t[0]) & t[1]); | ||
| 42 | x[1] = t[1] ^ ((~t[2]) & t[3]); | ||
| 43 | x[3] = t[3] ^ ((~t[4]) & t[0]); | ||
| 44 | x[1] ^= x[0]; | ||
| 45 | t[1] = x[1]; | ||
| 46 | x[1] = math.rotr(u64, x[1], 39); | ||
| 47 | x[3] ^= x[2]; | ||
| 48 | t[2] = x[2]; | ||
| 49 | x[2] = math.rotr(u64, x[2], 1); | ||
| 50 | t[4] = x[4]; | ||
| 51 | t[2] ^= x[2]; | ||
| 52 | x[2] = math.rotr(u64, x[2], 5); | ||
| 53 | t[3] = x[3]; | ||
| 54 | t[1] ^= x[1]; | ||
| 55 | x[3] = math.rotr(u64, x[3], 10); | ||
| 56 | x[0] ^= x[4]; | ||
| 57 | x[4] = math.rotr(u64, x[4], 7); | ||
| 58 | t[3] ^= x[3]; | ||
| 59 | x[2] ^= t[2]; | ||
| 60 | x[1] = math.rotr(u64, x[1], 22); | ||
| 61 | t[0] = x[0]; | ||
| 62 | x[2] = ~x[2]; | ||
| 63 | x[3] = math.rotr(u64, x[3], 7); | ||
| 64 | t[4] ^= x[4]; | ||
| 65 | x[4] = math.rotr(u64, x[4], 34); | ||
| 66 | x[3] ^= t[3]; | ||
| 67 | x[1] ^= t[1]; | ||
| 68 | x[0] = math.rotr(u64, x[0], 19); | ||
| 69 | x[4] ^= t[4]; | ||
| 70 | t[0] ^= x[0]; | ||
| 71 | x[0] = math.rotr(u64, x[0], 9); | ||
| 72 | x[0] ^= t[0]; | ||
| 73 | } | ||
| 74 | |||
| 75 | fn p12(isap: *IsapA128A) void { | ||
| 76 | const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b }; | ||
| 77 | inline for (rks) |rk| { | ||
| 78 | isap.round(rk); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | fn p6(isap: *IsapA128A) void { | ||
| 83 | const rks = [6]u64{ 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b }; | ||
| 84 | inline for (rks) |rk| { | ||
| 85 | isap.round(rk); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | fn p1(isap: *IsapA128A) void { | ||
| 90 | isap.round(0x4b); | ||
| 91 | } | ||
| 92 | 31 | ||
| 93 | fn absorb(isap: *IsapA128A, m: []const u8) void { | 32 | fn absorb(isap: *IsapA128A, m: []const u8) void { |
| 94 | var block = &isap.block; | ||
| 95 | var i: usize = 0; | 33 | var i: usize = 0; |
| 96 | while (true) : (i += 8) { | 34 | while (true) : (i += 8) { |
| 97 | const left = m.len - i; | 35 | const left = m.len - i; |
| 98 | if (left >= 8) { | 36 | if (left >= 8) { |
| 99 | block[0] ^= mem.readIntBig(u64, m[i..][0..8]); | 37 | isap.st.addBytes(m[i..][0..8]); |
| 100 | isap.p12(); | 38 | isap.st.permute(); |
| 101 | if (left == 8) { | 39 | if (left == 8) { |
| 102 | block[0] ^= 0x8000000000000000; | 40 | isap.st.addByte(0x80, 0); |
| 103 | isap.p12(); | 41 | isap.st.permute(); |
| 104 | break; | 42 | break; |
| 105 | } | 43 | } |
| 106 | } else { | 44 | } else { |
| 107 | var padded = [_]u8{0} ** 8; | 45 | var padded = [_]u8{0} ** 8; |
| 108 | mem.copy(u8, padded[0..left], m[i..]); | 46 | mem.copy(u8, padded[0..left], m[i..]); |
| 109 | padded[left] = 0x80; | 47 | padded[left] = 0x80; |
| 110 | block[0] ^= mem.readIntBig(u64, padded[0..]); | 48 | isap.st.addBytes(&padded); |
| 111 | isap.p12(); | 49 | isap.st.permute(); |
| 112 | break; | 50 | break; |
| 113 | } | 51 | } |
| 114 | } | 52 | } |
| ... | @@ -116,65 +54,59 @@ pub const IsapA128A = struct { | ... | @@ -116,65 +54,59 @@ pub const IsapA128A = struct { |
| 116 | 54 | ||
| 117 | fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 { | 55 | fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 { |
| 118 | var isap = IsapA128A{ | 56 | var isap = IsapA128A{ |
| 119 | .block = Block{ | 57 | .st = Ascon.initFromWords(.{ |
| 120 | mem.readIntBig(u64, k[0..8]), | 58 | mem.readIntBig(u64, k[0..8]), |
| 121 | mem.readIntBig(u64, k[8..16]), | 59 | mem.readIntBig(u64, k[8..16]), |
| 122 | mem.readIntBig(u64, iv[0..8]), | 60 | mem.readIntBig(u64, iv[0..8]), |
| 123 | 0, | 61 | 0, |
| 124 | 0, | 62 | 0, |
| 125 | }, | 63 | }), |
| 126 | }; | 64 | }; |
| 127 | isap.p12(); | 65 | isap.st.permute(); |
| 128 | 66 | ||
| 129 | var i: usize = 0; | 67 | var i: usize = 0; |
| 130 | while (i < y.len * 8 - 1) : (i += 1) { | 68 | while (i < y.len * 8 - 1) : (i += 1) { |
| 131 | const cur_byte_pos = i / 8; | 69 | const cur_byte_pos = i / 8; |
| 132 | const cur_bit_pos = @truncate(u3, 7 - (i % 8)); | 70 | const cur_bit_pos = @truncate(u3, 7 - (i % 8)); |
| 133 | const cur_bit = @as(u64, ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7); | 71 | const cur_bit = ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7; |
| 134 | isap.block[0] ^= cur_bit << 56; | 72 | isap.st.addByte(cur_bit, 0); |
| 135 | isap.p1(); | 73 | isap.st.permuteR(1); |
| 136 | } | 74 | } |
| 137 | const cur_bit = @as(u64, (y[y.len - 1] & 1) << 7); | 75 | const cur_bit = (y[y.len - 1] & 1) << 7; |
| 138 | isap.block[0] ^= cur_bit << 56; | 76 | isap.st.addByte(cur_bit, 0); |
| 139 | isap.p12(); | 77 | isap.st.permute(); |
| 140 | 78 | ||
| 141 | var out: [out_len]u8 = undefined; | 79 | var out: [out_len]u8 = undefined; |
| 142 | var j: usize = 0; | 80 | isap.st.extractBytes(&out); |
| 143 | while (j < out_len) : (j += 8) { | 81 | isap.st.secureZero(); |
| 144 | mem.writeIntBig(u64, out[j..][0..8], isap.block[j / 8]); | ||
| 145 | } | ||
| 146 | std.crypto.utils.secureZero(u64, &isap.block); | ||
| 147 | return out; | 82 | return out; |
| 148 | } | 83 | } |
| 149 | 84 | ||
| 150 | fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 { | 85 | fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 { |
| 151 | var isap = IsapA128A{ | 86 | var isap = IsapA128A{ |
| 152 | .block = Block{ | 87 | .st = Ascon.initFromWords(.{ |
| 153 | mem.readIntBig(u64, npub[0..8]), | 88 | mem.readIntBig(u64, npub[0..8]), |
| 154 | mem.readIntBig(u64, npub[8..16]), | 89 | mem.readIntBig(u64, npub[8..16]), |
| 155 | mem.readIntBig(u64, iv1[0..]), | 90 | mem.readIntBig(u64, iv1[0..]), |
| 156 | 0, | 91 | 0, |
| 157 | 0, | 92 | 0, |
| 158 | }, | 93 | }), |
| 159 | }; | 94 | }; |
| 160 | isap.p12(); | 95 | isap.st.permute(); |
| 161 | 96 | ||
| 162 | isap.absorb(ad); | 97 | isap.absorb(ad); |
| 163 | isap.block[4] ^= 1; | 98 | isap.st.addByte(1, Ascon.block_bytes - 1); |
| 164 | isap.absorb(c); | 99 | isap.absorb(c); |
| 165 | 100 | ||
| 166 | var y: [16]u8 = undefined; | 101 | var y: [16]u8 = undefined; |
| 167 | mem.writeIntBig(u64, y[0..8], isap.block[0]); | 102 | isap.st.extractBytes(&y); |
| 168 | mem.writeIntBig(u64, y[8..16], isap.block[1]); | ||
| 169 | const nb = trickle(key, iv2, y[0..], 16); | 103 | const nb = trickle(key, iv2, y[0..], 16); |
| 170 | isap.block[0] = mem.readIntBig(u64, nb[0..8]); | 104 | isap.st.setBytes(&nb); |
| 171 | isap.block[1] = mem.readIntBig(u64, nb[8..16]); | 105 | isap.st.permute(); |
| 172 | isap.p12(); | ||
| 173 | 106 | ||
| 174 | var tag: [16]u8 = undefined; | 107 | var tag: [16]u8 = undefined; |
| 175 | mem.writeIntBig(u64, tag[0..8], isap.block[0]); | 108 | isap.st.extractBytes(&tag); |
| 176 | mem.writeIntBig(u64, tag[8..16], isap.block[1]); | 109 | isap.st.secureZero(); |
| 177 | std.crypto.utils.secureZero(u64, &isap.block); | ||
| 178 | return tag; | 110 | return tag; |
| 179 | } | 111 | } |
| 180 | 112 | ||
| ... | @@ -183,34 +115,31 @@ pub const IsapA128A = struct { | ... | @@ -183,34 +115,31 @@ pub const IsapA128A = struct { |
| 183 | 115 | ||
| 184 | const nb = trickle(key, iv3, npub[0..], 24); | 116 | const nb = trickle(key, iv3, npub[0..], 24); |
| 185 | var isap = IsapA128A{ | 117 | var isap = IsapA128A{ |
| 186 | .block = Block{ | 118 | .st = Ascon.initFromWords(.{ |
| 187 | mem.readIntBig(u64, nb[0..8]), | 119 | mem.readIntBig(u64, nb[0..8]), |
| 188 | mem.readIntBig(u64, nb[8..16]), | 120 | mem.readIntBig(u64, nb[8..16]), |
| 189 | mem.readIntBig(u64, nb[16..24]), | 121 | mem.readIntBig(u64, nb[16..24]), |
| 190 | mem.readIntBig(u64, npub[0..8]), | 122 | mem.readIntBig(u64, npub[0..8]), |
| 191 | mem.readIntBig(u64, npub[8..16]), | 123 | mem.readIntBig(u64, npub[8..16]), |
| 192 | }, | 124 | }), |
| 193 | }; | 125 | }; |
| 194 | isap.p6(); | 126 | isap.st.permuteR(6); |
| 195 | 127 | ||
| 196 | var i: usize = 0; | 128 | var i: usize = 0; |
| 197 | while (true) : (i += 8) { | 129 | while (true) : (i += 8) { |
| 198 | const left = in.len - i; | 130 | const left = in.len - i; |
| 199 | if (left >= 8) { | 131 | if (left >= 8) { |
| 200 | mem.writeIntNative(u64, out[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, in[i..][0..8])); | 132 | isap.st.xorBytes(out[i..][0..8], in[i..][0..8]); |
| 201 | if (left == 8) { | 133 | if (left == 8) { |
| 202 | break; | 134 | break; |
| 203 | } | 135 | } |
| 204 | isap.p6(); | 136 | isap.st.permuteR(6); |
| 205 | } else { | 137 | } else { |
| 206 | var pad = [_]u8{0} ** 8; | 138 | isap.st.xorBytes(out[i..], in[i..]); |
| 207 | mem.copy(u8, pad[0..left], in[i..][0..left]); | ||
| 208 | mem.writeIntNative(u64, pad[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, pad[i..][0..8])); | ||
| 209 | mem.copy(u8, out[i..][0..left], pad[0..left]); | ||
| 210 | break; | 139 | break; |
| 211 | } | 140 | } |
| 212 | } | 141 | } |
| 213 | std.crypto.utils.secureZero(u64, &isap.block); | 142 | isap.st.secureZero(); |
| 214 | } | 143 | } |
| 215 | 144 | ||
| 216 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { | 145 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { |
| ... | @@ -220,12 +149,9 @@ pub const IsapA128A = struct { | ... | @@ -220,12 +149,9 @@ pub const IsapA128A = struct { |
| 220 | 149 | ||
| 221 | pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { | 150 | pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { |
| 222 | var computed_tag = mac(c, ad, npub, key); | 151 | var computed_tag = mac(c, ad, npub, key); |
| 223 | var acc: u8 = 0; | 152 | const res = crypto.utils.timingSafeEql([tag_length]u8, computed_tag, tag); |
| 224 | for (computed_tag) |_, j| { | 153 | crypto.utils.secureZero(u8, &computed_tag); |
| 225 | acc |= (computed_tag[j] ^ tag[j]); | 154 | if (!res) { |
| 226 | } | ||
| 227 | std.crypto.utils.secureZero(u8, &computed_tag); | ||
| 228 | if (acc != 0) { | ||
| 229 | return error.AuthenticationFailed; | 155 | return error.AuthenticationFailed; |
| 230 | } | 156 | } |
| 231 | xor(m, c, npub, key); | 157 | xor(m, c, npub, key); |
lib/std/fs.zig+1-1| ... | @@ -1763,7 +1763,7 @@ pub const Dir = struct { | ... | @@ -1763,7 +1763,7 @@ pub const Dir = struct { |
| 1763 | var nt_name = w.UNICODE_STRING{ | 1763 | var nt_name = w.UNICODE_STRING{ |
| 1764 | .Length = path_len_bytes, | 1764 | .Length = path_len_bytes, |
| 1765 | .MaximumLength = path_len_bytes, | 1765 | .MaximumLength = path_len_bytes, |
| 1766 | .Buffer = @qualCast([*:0]u16, sub_path_w), | 1766 | .Buffer = @constCast(sub_path_w), |
| 1767 | }; | 1767 | }; |
| 1768 | var attr = w.OBJECT_ATTRIBUTES{ | 1768 | var attr = w.OBJECT_ATTRIBUTES{ |
| 1769 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | 1769 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
lib/std/json.zig+56-11| ... | @@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList; | ... | @@ -1163,11 +1163,12 @@ const ArrayList = std.ArrayList; |
| 1163 | const StringArrayHashMap = std.StringArrayHashMap; | 1163 | const StringArrayHashMap = std.StringArrayHashMap; |
| 1164 | 1164 | ||
| 1165 | pub const ValueTree = struct { | 1165 | pub const ValueTree = struct { |
| 1166 | arena: ArenaAllocator, | 1166 | arena: *ArenaAllocator, |
| 1167 | root: Value, | 1167 | root: Value, |
| 1168 | 1168 | ||
| 1169 | pub fn deinit(self: *ValueTree) void { | 1169 | pub fn deinit(self: *ValueTree) void { |
| 1170 | self.arena.deinit(); | 1170 | self.arena.deinit(); |
| 1171 | self.arena.child_allocator.destroy(self.arena); | ||
| 1171 | } | 1172 | } |
| 1172 | }; | 1173 | }; |
| 1173 | 1174 | ||
| ... | @@ -1639,7 +1640,7 @@ fn parseInternal( | ... | @@ -1639,7 +1640,7 @@ fn parseInternal( |
| 1639 | const allocator = options.allocator orelse return error.AllocatorRequired; | 1640 | const allocator = options.allocator orelse return error.AllocatorRequired; |
| 1640 | switch (ptrInfo.size) { | 1641 | switch (ptrInfo.size) { |
| 1641 | .One => { | 1642 | .One => { |
| 1642 | const r: T = try allocator.create(ptrInfo.child); | 1643 | const r: *ptrInfo.child = try allocator.create(ptrInfo.child); |
| 1643 | errdefer allocator.destroy(r); | 1644 | errdefer allocator.destroy(r); |
| 1644 | r.* = try parseInternal(ptrInfo.child, token, tokens, options); | 1645 | r.* = try parseInternal(ptrInfo.child, token, tokens, options); |
| 1645 | return r; | 1646 | return r; |
| ... | @@ -1678,19 +1679,16 @@ fn parseInternal( | ... | @@ -1678,19 +1679,16 @@ fn parseInternal( |
| 1678 | if (ptrInfo.child != u8) return error.UnexpectedToken; | 1679 | if (ptrInfo.child != u8) return error.UnexpectedToken; |
| 1679 | const source_slice = stringToken.slice(tokens.slice, tokens.i - 1); | 1680 | const source_slice = stringToken.slice(tokens.slice, tokens.i - 1); |
| 1680 | const len = stringToken.decodedLength(); | 1681 | const len = stringToken.decodedLength(); |
| 1681 | const output = try allocator.alloc(u8, len + @boolToInt(ptrInfo.sentinel != null)); | 1682 | const output = if (ptrInfo.sentinel) |sentinel_ptr| |
| 1683 | try allocator.allocSentinel(u8, len, @ptrCast(*const u8, sentinel_ptr).*) | ||
| 1684 | else | ||
| 1685 | try allocator.alloc(u8, len); | ||
| 1682 | errdefer allocator.free(output); | 1686 | errdefer allocator.free(output); |
| 1683 | switch (stringToken.escapes) { | 1687 | switch (stringToken.escapes) { |
| 1684 | .None => mem.copy(u8, output, source_slice), | 1688 | .None => mem.copy(u8, output, source_slice), |
| 1685 | .Some => try unescapeValidString(output, source_slice), | 1689 | .Some => try unescapeValidString(output, source_slice), |
| 1686 | } | 1690 | } |
| 1687 | 1691 | ||
| 1688 | if (ptrInfo.sentinel) |some| { | ||
| 1689 | const char = @ptrCast(*const u8, some).*; | ||
| 1690 | output[len] = char; | ||
| 1691 | return output[0..len :char]; | ||
| 1692 | } | ||
| 1693 | |||
| 1694 | return output; | 1692 | return output; |
| 1695 | }, | 1693 | }, |
| 1696 | else => return error.UnexpectedToken, | 1694 | else => return error.UnexpectedToken, |
| ... | @@ -1744,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void { | ... | @@ -1744,7 +1742,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void { |
| 1744 | .Struct => |structInfo| { | 1742 | .Struct => |structInfo| { |
| 1745 | inline for (structInfo.fields) |field| { | 1743 | inline for (structInfo.fields) |field| { |
| 1746 | if (!field.is_comptime) { | 1744 | if (!field.is_comptime) { |
| 1747 | parseFree(field.type, @field(value, field.name), options); | 1745 | var should_free = true; |
| 1746 | if (field.default_value) |default| { | ||
| 1747 | switch (@typeInfo(field.type)) { | ||
| 1748 | // We must not attempt to free pointers to struct default values | ||
| 1749 | .Pointer => |fieldPtrInfo| { | ||
| 1750 | const field_value = @field(value, field.name); | ||
| 1751 | const field_ptr = switch (fieldPtrInfo.size) { | ||
| 1752 | .One => field_value, | ||
| 1753 | .Slice => field_value.ptr, | ||
| 1754 | else => unreachable, // Other pointer types are not parseable | ||
| 1755 | }; | ||
| 1756 | const field_addr = @ptrToInt(field_ptr); | ||
| 1757 | |||
| 1758 | const casted_default = @ptrCast(*const field.type, @alignCast(@alignOf(field.type), default)).*; | ||
| 1759 | const default_ptr = switch (fieldPtrInfo.size) { | ||
| 1760 | .One => casted_default, | ||
| 1761 | .Slice => casted_default.ptr, | ||
| 1762 | else => unreachable, // Other pointer types are not parseable | ||
| 1763 | }; | ||
| 1764 | const default_addr = @ptrToInt(default_ptr); | ||
| 1765 | |||
| 1766 | if (field_addr == default_addr) { | ||
| 1767 | should_free = false; | ||
| 1768 | } | ||
| 1769 | }, | ||
| 1770 | else => {}, | ||
| 1771 | } | ||
| 1772 | } | ||
| 1773 | if (should_free) { | ||
| 1774 | parseFree(field.type, @field(value, field.name), options); | ||
| 1775 | } | ||
| 1748 | } | 1776 | } |
| 1749 | } | 1777 | } |
| 1750 | }, | 1778 | }, |
| ... | @@ -1809,8 +1837,12 @@ pub const Parser = struct { | ... | @@ -1809,8 +1837,12 @@ pub const Parser = struct { |
| 1809 | pub fn parse(p: *Parser, input: []const u8) !ValueTree { | 1837 | pub fn parse(p: *Parser, input: []const u8) !ValueTree { |
| 1810 | var s = TokenStream.init(input); | 1838 | var s = TokenStream.init(input); |
| 1811 | 1839 | ||
| 1812 | var arena = ArenaAllocator.init(p.allocator); | 1840 | var arena = try p.allocator.create(ArenaAllocator); |
| 1841 | errdefer p.allocator.destroy(arena); | ||
| 1842 | |||
| 1843 | arena.* = ArenaAllocator.init(p.allocator); | ||
| 1813 | errdefer arena.deinit(); | 1844 | errdefer arena.deinit(); |
| 1845 | |||
| 1814 | const allocator = arena.allocator(); | 1846 | const allocator = arena.allocator(); |
| 1815 | 1847 | ||
| 1816 | while (try s.next()) |token| { | 1848 | while (try s.next()) |token| { |
| ... | @@ -2684,3 +2716,16 @@ test "encodesTo" { | ... | @@ -2684,3 +2716,16 @@ test "encodesTo" { |
| 2684 | try testing.expectEqual(true, encodesTo("😂", "\\ud83d\\ude02")); | 2716 | try testing.expectEqual(true, encodesTo("😂", "\\ud83d\\ude02")); |
| 2685 | try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02")); | 2717 | try testing.expectEqual(true, encodesTo("withąunicode😂", "with\\u0105unicode\\ud83d\\ude02")); |
| 2686 | } | 2718 | } |
| 2719 | |||
| 2720 | test "issue 14600" { | ||
| 2721 | const json = "\"\\n\""; | ||
| 2722 | var token_stream = std.json.TokenStream.init(json); | ||
| 2723 | const options = ParseOptions{ .allocator = std.testing.allocator }; | ||
| 2724 | |||
| 2725 | // Pre-fix, this line would panic: | ||
| 2726 | const result = try std.json.parse([:0]const u8, &token_stream, options); | ||
| 2727 | defer std.json.parseFree([:0]const u8, result, options); | ||
| 2728 | |||
| 2729 | // Double-check that we're getting the right result | ||
| 2730 | try testing.expect(mem.eql(u8, result, "\n")); | ||
| 2731 | } |
lib/std/json/test.zig+51| ... | @@ -2238,6 +2238,39 @@ test "parse into struct with no fields" { | ... | @@ -2238,6 +2238,39 @@ test "parse into struct with no fields" { |
| 2238 | try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{})); | 2238 | try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{})); |
| 2239 | } | 2239 | } |
| 2240 | 2240 | ||
| 2241 | const test_const_value: usize = 123; | ||
| 2242 | |||
| 2243 | test "parse into struct with default const pointer field" { | ||
| 2244 | const T = struct { a: *const usize = &test_const_value }; | ||
| 2245 | var ts = TokenStream.init("{}"); | ||
| 2246 | try testing.expectEqual(T{}, try parse(T, &ts, .{})); | ||
| 2247 | } | ||
| 2248 | |||
| 2249 | const test_default_usize: usize = 123; | ||
| 2250 | const test_default_usize_ptr: *align(1) const usize = &test_default_usize; | ||
| 2251 | const test_default_str: []const u8 = "test str"; | ||
| 2252 | const test_default_str_slice: [2][]const u8 = [_][]const u8{ | ||
| 2253 | "test1", | ||
| 2254 | "test2", | ||
| 2255 | }; | ||
| 2256 | |||
| 2257 | test "freeing parsed structs with pointers to default values" { | ||
| 2258 | const T = struct { | ||
| 2259 | int: *const usize = &test_default_usize, | ||
| 2260 | int_ptr: *allowzero align(1) const usize = test_default_usize_ptr, | ||
| 2261 | str: []const u8 = test_default_str, | ||
| 2262 | str_slice: []const []const u8 = &test_default_str_slice, | ||
| 2263 | }; | ||
| 2264 | |||
| 2265 | var ts = json.TokenStream.init("{}"); | ||
| 2266 | const options = .{ .allocator = std.heap.page_allocator }; | ||
| 2267 | const parsed = try json.parse(T, &ts, options); | ||
| 2268 | |||
| 2269 | try testing.expectEqual(T{}, parsed); | ||
| 2270 | |||
| 2271 | json.parseFree(T, parsed, options); | ||
| 2272 | } | ||
| 2273 | |||
| 2241 | test "parse into struct where destination and source lengths mismatch" { | 2274 | test "parse into struct where destination and source lengths mismatch" { |
| 2242 | const T = struct { a: [2]u8 }; | 2275 | const T = struct { a: [2]u8 }; |
| 2243 | var ts = TokenStream.init("{\"a\": \"bbb\"}"); | 2276 | var ts = TokenStream.init("{\"a\": \"bbb\"}"); |
| ... | @@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" { | ... | @@ -2581,6 +2614,24 @@ test "parsing empty string gives appropriate error" { |
| 2581 | try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), "")); | 2614 | try testing.expectError(error.UnexpectedEndOfJson, testParse(arena_allocator.allocator(), "")); |
| 2582 | } | 2615 | } |
| 2583 | 2616 | ||
| 2617 | test "parse tree should not contain dangling pointers" { | ||
| 2618 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); | ||
| 2619 | defer arena_allocator.deinit(); | ||
| 2620 | |||
| 2621 | var p = json.Parser.init(arena_allocator.allocator(), false); | ||
| 2622 | defer p.deinit(); | ||
| 2623 | |||
| 2624 | var tree = try p.parse("[]"); | ||
| 2625 | defer tree.deinit(); | ||
| 2626 | |||
| 2627 | // Allocation should succeed | ||
| 2628 | var i: usize = 0; | ||
| 2629 | while (i < 100) : (i += 1) { | ||
| 2630 | try tree.root.Array.append(std.json.Value{ .Integer = 100 }); | ||
| 2631 | } | ||
| 2632 | try testing.expectEqual(tree.root.Array.items.len, 100); | ||
| 2633 | } | ||
| 2634 | |||
| 2584 | test "integer after float has proper type" { | 2635 | test "integer after float has proper type" { |
| 2585 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); | 2636 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); |
| 2586 | defer arena_allocator.deinit(); | 2637 | defer arena_allocator.deinit(); |
lib/std/mem/Allocator.zig+2-2| ... | @@ -112,7 +112,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void { | ... | @@ -112,7 +112,7 @@ pub fn destroy(self: Allocator, ptr: anytype) void { |
| 112 | const info = @typeInfo(@TypeOf(ptr)).Pointer; | 112 | const info = @typeInfo(@TypeOf(ptr)).Pointer; |
| 113 | const T = info.child; | 113 | const T = info.child; |
| 114 | if (@sizeOf(T) == 0) return; | 114 | if (@sizeOf(T) == 0) return; |
| 115 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr)); | 115 | const non_const_ptr = @ptrCast([*]u8, @constCast(ptr)); |
| 116 | self.rawFree(non_const_ptr[0..@sizeOf(T)], math.log2(info.alignment), @returnAddress()); | 116 | self.rawFree(non_const_ptr[0..@sizeOf(T)], math.log2(info.alignment), @returnAddress()); |
| 117 | } | 117 | } |
| 118 | 118 | ||
| ... | @@ -297,7 +297,7 @@ pub fn free(self: Allocator, memory: anytype) void { | ... | @@ -297,7 +297,7 @@ pub fn free(self: Allocator, memory: anytype) void { |
| 297 | const bytes = mem.sliceAsBytes(memory); | 297 | const bytes = mem.sliceAsBytes(memory); |
| 298 | const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0; | 298 | const bytes_len = bytes.len + if (Slice.sentinel != null) @sizeOf(Slice.child) else 0; |
| 299 | if (bytes_len == 0) return; | 299 | if (bytes_len == 0) return; |
| 300 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); | 300 | const non_const_ptr = @constCast(bytes.ptr); |
| 301 | // TODO: https://github.com/ziglang/zig/issues/4298 | 301 | // TODO: https://github.com/ziglang/zig/issues/4298 |
| 302 | @memset(non_const_ptr, undefined, bytes_len); | 302 | @memset(non_const_ptr, undefined, bytes_len); |
| 303 | self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress()); | 303 | self.rawFree(non_const_ptr[0..bytes_len], log2a(Slice.alignment), @returnAddress()); |
lib/std/meta.zig+1-1| ... | @@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type { | ... | @@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type { |
| 332 | @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T)); | 332 | @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T)); |
| 333 | } | 333 | } |
| 334 | 334 | ||
| 335 | const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()"); | 335 | pub const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()"); |
| 336 | 336 | ||
| 337 | pub fn containerLayout(comptime T: type) Type.ContainerLayout { | 337 | pub fn containerLayout(comptime T: type) Type.ContainerLayout { |
| 338 | return switch (@typeInfo(T)) { | 338 | return switch (@typeInfo(T)) { |
lib/std/os.zig+1-1| ... | @@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32 | ... | @@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32 |
| 4513 | var nt_name = windows.UNICODE_STRING{ | 4513 | var nt_name = windows.UNICODE_STRING{ |
| 4514 | .Length = path_len_bytes, | 4514 | .Length = path_len_bytes, |
| 4515 | .MaximumLength = path_len_bytes, | 4515 | .MaximumLength = path_len_bytes, |
| 4516 | .Buffer = @qualCast([*:0]u16, sub_path_w), | 4516 | .Buffer = @constCast(sub_path_w), |
| 4517 | }; | 4517 | }; |
| 4518 | var attr = windows.OBJECT_ATTRIBUTES{ | 4518 | var attr = windows.OBJECT_ATTRIBUTES{ |
| 4519 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | 4519 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), |
lib/std/os/linux/syscalls.zig+10-6| ... | @@ -2057,7 +2057,7 @@ pub const Mips64 = enum(usize) { | ... | @@ -2057,7 +2057,7 @@ pub const Mips64 = enum(usize) { |
| 2057 | writev = Linux + 19, | 2057 | writev = Linux + 19, |
| 2058 | access = Linux + 20, | 2058 | access = Linux + 20, |
| 2059 | pipe = Linux + 21, | 2059 | pipe = Linux + 21, |
| 2060 | select = Linux + 22, | 2060 | _newselect = Linux + 22, |
| 2061 | sched_yield = Linux + 23, | 2061 | sched_yield = Linux + 23, |
| 2062 | mremap = Linux + 24, | 2062 | mremap = Linux + 24, |
| 2063 | msync = Linux + 25, | 2063 | msync = Linux + 25, |
| ... | @@ -2071,8 +2071,8 @@ pub const Mips64 = enum(usize) { | ... | @@ -2071,8 +2071,8 @@ pub const Mips64 = enum(usize) { |
| 2071 | pause = Linux + 33, | 2071 | pause = Linux + 33, |
| 2072 | nanosleep = Linux + 34, | 2072 | nanosleep = Linux + 34, |
| 2073 | getitimer = Linux + 35, | 2073 | getitimer = Linux + 35, |
| 2074 | alarm = Linux + 36, | 2074 | setitimer = Linux + 36, |
| 2075 | setitimer = Linux + 37, | 2075 | alarm = Linux + 37, |
| 2076 | getpid = Linux + 38, | 2076 | getpid = Linux + 38, |
| 2077 | sendfile = Linux + 39, | 2077 | sendfile = Linux + 39, |
| 2078 | socket = Linux + 40, | 2078 | socket = Linux + 40, |
| ... | @@ -2286,7 +2286,7 @@ pub const Mips64 = enum(usize) { | ... | @@ -2286,7 +2286,7 @@ pub const Mips64 = enum(usize) { |
| 2286 | mknodat = Linux + 249, | 2286 | mknodat = Linux + 249, |
| 2287 | fchownat = Linux + 250, | 2287 | fchownat = Linux + 250, |
| 2288 | futimesat = Linux + 251, | 2288 | futimesat = Linux + 251, |
| 2289 | newfstatat = Linux + 252, | 2289 | fstatat64 = Linux + 252, |
| 2290 | unlinkat = Linux + 253, | 2290 | unlinkat = Linux + 253, |
| 2291 | renameat = Linux + 254, | 2291 | renameat = Linux + 254, |
| 2292 | linkat = Linux + 255, | 2292 | linkat = Linux + 255, |
| ... | @@ -2315,8 +2315,8 @@ pub const Mips64 = enum(usize) { | ... | @@ -2315,8 +2315,8 @@ pub const Mips64 = enum(usize) { |
| 2315 | eventfd = Linux + 278, | 2315 | eventfd = Linux + 278, |
| 2316 | fallocate = Linux + 279, | 2316 | fallocate = Linux + 279, |
| 2317 | timerfd_create = Linux + 280, | 2317 | timerfd_create = Linux + 280, |
| 2318 | timerfd_settime = Linux + 281, | 2318 | timerfd_gettime = Linux + 281, |
| 2319 | timerfd_gettime = Linux + 282, | 2319 | timerfd_settime = Linux + 282, |
| 2320 | signalfd4 = Linux + 283, | 2320 | signalfd4 = Linux + 283, |
| 2321 | eventfd2 = Linux + 284, | 2321 | eventfd2 = Linux + 284, |
| 2322 | epoll_create1 = Linux + 285, | 2322 | epoll_create1 = Linux + 285, |
| ... | @@ -2382,9 +2382,13 @@ pub const Mips64 = enum(usize) { | ... | @@ -2382,9 +2382,13 @@ pub const Mips64 = enum(usize) { |
| 2382 | process_madvise = Linux + 440, | 2382 | process_madvise = Linux + 440, |
| 2383 | epoll_pwait2 = Linux + 441, | 2383 | epoll_pwait2 = Linux + 441, |
| 2384 | mount_setattr = Linux + 442, | 2384 | mount_setattr = Linux + 442, |
| 2385 | quotactl_fd = Linux + 443, | ||
| 2385 | landlock_create_ruleset = Linux + 444, | 2386 | landlock_create_ruleset = Linux + 444, |
| 2386 | landlock_add_rule = Linux + 445, | 2387 | landlock_add_rule = Linux + 445, |
| 2387 | landlock_restrict_self = Linux + 446, | 2388 | landlock_restrict_self = Linux + 446, |
| 2389 | process_mrelease = Linux + 448, | ||
| 2390 | futex_waitv = Linux + 449, | ||
| 2391 | set_mempolicy_home_node = Linux + 450, | ||
| 2388 | }; | 2392 | }; |
| 2389 | 2393 | ||
| 2390 | pub const PowerPC = enum(usize) { | 2394 | pub const PowerPC = enum(usize) { |
lib/std/os/windows.zig+7-7| ... | @@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 85 | var nt_name = UNICODE_STRING{ | 85 | var nt_name = UNICODE_STRING{ |
| 86 | .Length = path_len_bytes, | 86 | .Length = path_len_bytes, |
| 87 | .MaximumLength = path_len_bytes, | 87 | .MaximumLength = path_len_bytes, |
| 88 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | 88 | .Buffer = @constCast(sub_path_w.ptr), |
| 89 | }; | 89 | }; |
| 90 | var attr = OBJECT_ATTRIBUTES{ | 90 | var attr = OBJECT_ATTRIBUTES{ |
| 91 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | 91 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | @@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void | ... | @@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void |
| 634 | var nt_name = UNICODE_STRING{ | 634 | var nt_name = UNICODE_STRING{ |
| 635 | .Length = path_len_bytes, | 635 | .Length = path_len_bytes, |
| 636 | .MaximumLength = path_len_bytes, | 636 | .MaximumLength = path_len_bytes, |
| 637 | .Buffer = @qualCast([*]u16, path_name.ptr), | 637 | .Buffer = @constCast(path_name.ptr), |
| 638 | }; | 638 | }; |
| 639 | 639 | ||
| 640 | const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name); | 640 | const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name); |
| ... | @@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin | ... | @@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin |
| 766 | var nt_name = UNICODE_STRING{ | 766 | var nt_name = UNICODE_STRING{ |
| 767 | .Length = path_len_bytes, | 767 | .Length = path_len_bytes, |
| 768 | .MaximumLength = path_len_bytes, | 768 | .MaximumLength = path_len_bytes, |
| 769 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | 769 | .Buffer = @constCast(sub_path_w.ptr), |
| 770 | }; | 770 | }; |
| 771 | var attr = OBJECT_ATTRIBUTES{ | 771 | var attr = OBJECT_ATTRIBUTES{ |
| 772 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | 772 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | @@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil | ... | @@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil |
| 876 | .Length = path_len_bytes, | 876 | .Length = path_len_bytes, |
| 877 | .MaximumLength = path_len_bytes, | 877 | .MaximumLength = path_len_bytes, |
| 878 | // The Windows API makes this mutable, but it will not mutate here. | 878 | // The Windows API makes this mutable, but it will not mutate here. |
| 879 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | 879 | .Buffer = @constCast(sub_path_w.ptr), |
| 880 | }; | 880 | }; |
| 881 | 881 | ||
| 882 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | 882 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| ... | @@ -1414,7 +1414,7 @@ pub fn sendmsg( | ... | @@ -1414,7 +1414,7 @@ pub fn sendmsg( |
| 1414 | } | 1414 | } |
| 1415 | 1415 | ||
| 1416 | pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 { | 1416 | pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 { |
| 1417 | var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @qualCast([*]u8, buf) }; | 1417 | var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @constCast(buf) }; |
| 1418 | var bytes_send: DWORD = undefined; | 1418 | var bytes_send: DWORD = undefined; |
| 1419 | if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) { | 1419 | if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) { |
| 1420 | return ws2_32.SOCKET_ERROR; | 1420 | return ws2_32.SOCKET_ERROR; |
| ... | @@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool { | ... | @@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool { |
| 1876 | const a_string = UNICODE_STRING{ | 1876 | const a_string = UNICODE_STRING{ |
| 1877 | .Length = a_bytes, | 1877 | .Length = a_bytes, |
| 1878 | .MaximumLength = a_bytes, | 1878 | .MaximumLength = a_bytes, |
| 1879 | .Buffer = @qualCast([*]u16, a.ptr), | 1879 | .Buffer = @constCast(a.ptr), |
| 1880 | }; | 1880 | }; |
| 1881 | const b_bytes = @intCast(u16, b.len * 2); | 1881 | const b_bytes = @intCast(u16, b.len * 2); |
| 1882 | const b_string = UNICODE_STRING{ | 1882 | const b_string = UNICODE_STRING{ |
| 1883 | .Length = b_bytes, | 1883 | .Length = b_bytes, |
| 1884 | .MaximumLength = b_bytes, | 1884 | .MaximumLength = b_bytes, |
| 1885 | .Buffer = @qualCast([*]u16, b.ptr), | 1885 | .Buffer = @constCast(b.ptr), |
| 1886 | }; | 1886 | }; |
| 1887 | return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE; | 1887 | return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE; |
| 1888 | } | 1888 | } |
lib/std/rand.zig+2-1| ... | @@ -18,8 +18,9 @@ const maxInt = std.math.maxInt; | ... | @@ -18,8 +18,9 @@ const maxInt = std.math.maxInt; |
| 18 | pub const DefaultPrng = Xoshiro256; | 18 | pub const DefaultPrng = Xoshiro256; |
| 19 | 19 | ||
| 20 | /// Cryptographically secure random numbers. | 20 | /// Cryptographically secure random numbers. |
| 21 | pub const DefaultCsprng = Xoodoo; | 21 | pub const DefaultCsprng = Ascon; |
| 22 | 22 | ||
| 23 | pub const Ascon = @import("rand/Ascon.zig"); | ||
| 23 | pub const Isaac64 = @import("rand/Isaac64.zig"); | 24 | pub const Isaac64 = @import("rand/Isaac64.zig"); |
| 24 | pub const Xoodoo = @import("rand/Xoodoo.zig"); | 25 | pub const Xoodoo = @import("rand/Xoodoo.zig"); |
| 25 | pub const Pcg = @import("rand/Pcg.zig"); | 26 | pub const Pcg = @import("rand/Pcg.zig"); |
lib/std/rand/Ascon.zig created+45| ... | @@ -0,0 +1,45 @@ | ||
| 1 | //! CSPRNG based on the Ascon XOFa construction | ||
| 2 | |||
| 3 | const std = @import("std"); | ||
| 4 | const min = std.math.min; | ||
| 5 | const mem = std.mem; | ||
| 6 | const Random = std.rand.Random; | ||
| 7 | const Self = @This(); | ||
| 8 | |||
| 9 | state: std.crypto.core.Ascon(.Little), | ||
| 10 | |||
| 11 | const rate = 8; | ||
| 12 | pub const secret_seed_length = 32; | ||
| 13 | |||
| 14 | /// The seed must be uniform, secret and `secret_seed_length` bytes long. | ||
| 15 | pub fn init(secret_seed: [secret_seed_length]u8) Self { | ||
| 16 | var state = std.crypto.core.Ascon(.Little).initXofA(); | ||
| 17 | var i: usize = 0; | ||
| 18 | while (i + rate <= secret_seed.len) : (i += rate) { | ||
| 19 | state.addBytes(secret_seed[i..][0..rate]); | ||
| 20 | state.permuteR(8); | ||
| 21 | } | ||
| 22 | const left = secret_seed.len - i; | ||
| 23 | if (left > 0) state.addBytes(secret_seed[i..]); | ||
| 24 | state.addByte(0x80, left); | ||
| 25 | state.permute(); | ||
| 26 | return Self{ .state = state }; | ||
| 27 | } | ||
| 28 | |||
| 29 | pub fn random(self: *Self) Random { | ||
| 30 | return Random.init(self, fill); | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn fill(self: *Self, buf: []u8) void { | ||
| 34 | var i: usize = 0; | ||
| 35 | while (true) { | ||
| 36 | const left = buf.len - i; | ||
| 37 | const n = min(left, rate); | ||
| 38 | self.state.extractBytes(buf[i..][0..n]); | ||
| 39 | if (left == 0) break; | ||
| 40 | self.state.permuteR(8); | ||
| 41 | i += n; | ||
| 42 | } | ||
| 43 | self.state.clear(0, rate); | ||
| 44 | self.state.permuteR(8); | ||
| 45 | } | ||
lib/std/zig/Ast.zig+1-1| ... | @@ -207,7 +207,7 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { | ... | @@ -207,7 +207,7 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void { |
| 207 | return stream.writeAll("declarations are not allowed between container fields"); | 207 | return stream.writeAll("declarations are not allowed between container fields"); |
| 208 | }, | 208 | }, |
| 209 | .expected_block => { | 209 | .expected_block => { |
| 210 | return stream.print("expected block or field, found '{s}'", .{ | 210 | return stream.print("expected block, found '{s}'", .{ |
| 211 | token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), | 211 | token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(), |
| 212 | }); | 212 | }); |
| 213 | }, | 213 | }, |
lib/std/zig/c_translation.zig+4-2| ... | @@ -74,8 +74,10 @@ fn castPtr(comptime DestType: type, target: anytype) DestType { | ... | @@ -74,8 +74,10 @@ fn castPtr(comptime DestType: type, target: anytype) DestType { |
| 74 | const dest = ptrInfo(DestType); | 74 | const dest = ptrInfo(DestType); |
| 75 | const source = ptrInfo(@TypeOf(target)); | 75 | const source = ptrInfo(@TypeOf(target)); |
| 76 | 76 | ||
| 77 | if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile) | 77 | if (source.is_const and !dest.is_const) |
| 78 | return @qualCast(DestType, target) | 78 | return @constCast(target) |
| 79 | else if (source.is_volatile and !dest.is_volatile) | ||
| 80 | return @volatileCast(target) | ||
| 79 | else if (@typeInfo(dest.child) == .Opaque) | 81 | else if (@typeInfo(dest.child) == .Opaque) |
| 80 | // dest.alignment would error out | 82 | // dest.alignment would error out |
| 81 | return @ptrCast(DestType, target) | 83 | return @ptrCast(DestType, target) |
lib/std/zig/render.zig-8| ... | @@ -2807,14 +2807,6 @@ fn nodeIsBlock(tag: Ast.Node.Tag) bool { | ... | @@ -2807,14 +2807,6 @@ fn nodeIsBlock(tag: Ast.Node.Tag) bool { |
| 2807 | .block_semicolon, | 2807 | .block_semicolon, |
| 2808 | .block_two, | 2808 | .block_two, |
| 2809 | .block_two_semicolon, | 2809 | .block_two_semicolon, |
| 2810 | .struct_init_dot, | ||
| 2811 | .struct_init_dot_comma, | ||
| 2812 | .struct_init_dot_two, | ||
| 2813 | .struct_init_dot_two_comma, | ||
| 2814 | .array_init_dot, | ||
| 2815 | .array_init_dot_comma, | ||
| 2816 | .array_init_dot_two, | ||
| 2817 | .array_init_dot_two_comma, | ||
| 2818 | => true, | 2810 | => true, |
| 2819 | else => false, | 2811 | else => false, |
| 2820 | }; | 2812 | }; |
lib/std/zig/tokenizer.zig+2-1| ... | @@ -739,6 +739,7 @@ pub const Tokenizer = struct { | ... | @@ -739,6 +739,7 @@ pub const Tokenizer = struct { |
| 739 | }, | 739 | }, |
| 740 | 0 => { | 740 | 0 => { |
| 741 | if (self.index == self.buffer.len) { | 741 | if (self.index == self.buffer.len) { |
| 742 | result.tag = .invalid; | ||
| 742 | break; | 743 | break; |
| 743 | } else { | 744 | } else { |
| 744 | self.checkLiteralCharacter(); | 745 | self.checkLiteralCharacter(); |
| ... | @@ -1326,7 +1327,7 @@ test "newline in string literal" { | ... | @@ -1326,7 +1327,7 @@ test "newline in string literal" { |
| 1326 | try testTokenize( | 1327 | try testTokenize( |
| 1327 | \\" | 1328 | \\" |
| 1328 | \\" | 1329 | \\" |
| 1329 | , &.{ .invalid, .string_literal }); | 1330 | , &.{ .invalid, .invalid }); |
| 1330 | } | 1331 | } |
| 1331 | 1332 | ||
| 1332 | test "code point literal with unicode escapes" { | 1333 | test "code point literal with unicode escapes" { |
src/AstGen.zig+18-3| ... | @@ -1730,7 +1730,8 @@ fn structInitExprRlNone( | ... | @@ -1730,7 +1730,8 @@ fn structInitExprRlNone( |
| 1730 | .container_type = ty_inst, | 1730 | .container_type = ty_inst, |
| 1731 | .name_start = str_index, | 1731 | .name_start = str_index, |
| 1732 | }) } } | 1732 | }) } } |
| 1733 | else .{ .rl = .none }; | 1733 | else |
| 1734 | .{ .rl = .none }; | ||
| 1734 | setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{ | 1735 | setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{ |
| 1735 | .field_name = str_index, | 1736 | .field_name = str_index, |
| 1736 | .init = try expr(gz, scope, sub_ri, field_init), | 1737 | .init = try expr(gz, scope, sub_ri, field_init), |
| ... | @@ -2530,7 +2531,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2530,7 +2531,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2530 | .bit_size_of, | 2531 | .bit_size_of, |
| 2531 | .typeof_log2_int_type, | 2532 | .typeof_log2_int_type, |
| 2532 | .ptr_to_int, | 2533 | .ptr_to_int, |
| 2533 | .qual_cast, | ||
| 2534 | .align_of, | 2534 | .align_of, |
| 2535 | .bool_to_int, | 2535 | .bool_to_int, |
| 2536 | .embed_file, | 2536 | .embed_file, |
| ... | @@ -8038,7 +8038,6 @@ fn builtinCall( | ... | @@ -8038,7 +8038,6 @@ fn builtinCall( |
| 8038 | .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast), | 8038 | .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast), |
| 8039 | .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast), | 8039 | .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast), |
| 8040 | .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast), | 8040 | .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast), |
| 8041 | .qual_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .qual_cast), | ||
| 8042 | .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate), | 8041 | .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate), |
| 8043 | // zig fmt: on | 8042 | // zig fmt: on |
| 8044 | 8043 | ||
| ... | @@ -8114,6 +8113,22 @@ fn builtinCall( | ... | @@ -8114,6 +8113,22 @@ fn builtinCall( |
| 8114 | }); | 8113 | }); |
| 8115 | return rvalue(gz, ri, result, node); | 8114 | return rvalue(gz, ri, result, node); |
| 8116 | }, | 8115 | }, |
| 8116 | .const_cast => { | ||
| 8117 | const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); | ||
| 8118 | const result = try gz.addExtendedPayload(.const_cast, Zir.Inst.UnNode{ | ||
| 8119 | .node = gz.nodeIndexToRelative(node), | ||
| 8120 | .operand = operand, | ||
| 8121 | }); | ||
| 8122 | return rvalue(gz, ri, result, node); | ||
| 8123 | }, | ||
| 8124 | .volatile_cast => { | ||
| 8125 | const operand = try expr(gz, scope, .{ .rl = .none }, params[0]); | ||
| 8126 | const result = try gz.addExtendedPayload(.volatile_cast, Zir.Inst.UnNode{ | ||
| 8127 | .node = gz.nodeIndexToRelative(node), | ||
| 8128 | .operand = operand, | ||
| 8129 | }); | ||
| 8130 | return rvalue(gz, ri, result, node); | ||
| 8131 | }, | ||
| 8117 | 8132 | ||
| 8118 | // zig fmt: off | 8133 | // zig fmt: off |
| 8119 | .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl), | 8134 | .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl), |
src/Autodoc.zig+2-1| ... | @@ -1400,7 +1400,6 @@ fn walkInstruction( | ... | @@ -1400,7 +1400,6 @@ fn walkInstruction( |
| 1400 | .float_cast, | 1400 | .float_cast, |
| 1401 | .int_cast, | 1401 | .int_cast, |
| 1402 | .ptr_cast, | 1402 | .ptr_cast, |
| 1403 | .qual_cast, | ||
| 1404 | .truncate, | 1403 | .truncate, |
| 1405 | .align_cast, | 1404 | .align_cast, |
| 1406 | .has_decl, | 1405 | .has_decl, |
| ... | @@ -2983,6 +2982,8 @@ fn walkInstruction( | ... | @@ -2983,6 +2982,8 @@ fn walkInstruction( |
| 2983 | .error_to_int, | 2982 | .error_to_int, |
| 2984 | .int_to_error, | 2983 | .int_to_error, |
| 2985 | .reify, | 2984 | .reify, |
| 2985 | .const_cast, | ||
| 2986 | .volatile_cast, | ||
| 2986 | => { | 2987 | => { |
| 2987 | const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data; | 2988 | const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 2988 | const bin_index = self.exprs.items.len; | 2989 | const bin_index = self.exprs.items.len; |
src/BuiltinFn.zig+16-8| ... | @@ -28,6 +28,7 @@ pub const Tag = enum { | ... | @@ -28,6 +28,7 @@ pub const Tag = enum { |
| 28 | cmpxchg_weak, | 28 | cmpxchg_weak, |
| 29 | compile_error, | 29 | compile_error, |
| 30 | compile_log, | 30 | compile_log, |
| 31 | const_cast, | ||
| 31 | ctz, | 32 | ctz, |
| 32 | c_undef, | 33 | c_undef, |
| 33 | c_va_arg, | 34 | c_va_arg, |
| ... | @@ -75,7 +76,6 @@ pub const Tag = enum { | ... | @@ -75,7 +76,6 @@ pub const Tag = enum { |
| 75 | prefetch, | 76 | prefetch, |
| 76 | ptr_cast, | 77 | ptr_cast, |
| 77 | ptr_to_int, | 78 | ptr_to_int, |
| 78 | qual_cast, | ||
| 79 | rem, | 79 | rem, |
| 80 | return_address, | 80 | return_address, |
| 81 | select, | 81 | select, |
| ... | @@ -116,6 +116,7 @@ pub const Tag = enum { | ... | @@ -116,6 +116,7 @@ pub const Tag = enum { |
| 116 | TypeOf, | 116 | TypeOf, |
| 117 | union_init, | 117 | union_init, |
| 118 | Vector, | 118 | Vector, |
| 119 | volatile_cast, | ||
| 119 | }; | 120 | }; |
| 120 | 121 | ||
| 121 | pub const MemLocRequirement = enum { | 122 | pub const MemLocRequirement = enum { |
| ... | @@ -345,6 +346,13 @@ pub const list = list: { | ... | @@ -345,6 +346,13 @@ pub const list = list: { |
| 345 | .param_count = null, | 346 | .param_count = null, |
| 346 | }, | 347 | }, |
| 347 | }, | 348 | }, |
| 349 | .{ | ||
| 350 | "@constCast", | ||
| 351 | .{ | ||
| 352 | .tag = .const_cast, | ||
| 353 | .param_count = 1, | ||
| 354 | }, | ||
| 355 | }, | ||
| 348 | .{ | 356 | .{ |
| 349 | "@ctz", | 357 | "@ctz", |
| 350 | .{ | 358 | .{ |
| ... | @@ -675,13 +683,6 @@ pub const list = list: { | ... | @@ -675,13 +683,6 @@ pub const list = list: { |
| 675 | .param_count = 1, | 683 | .param_count = 1, |
| 676 | }, | 684 | }, |
| 677 | }, | 685 | }, |
| 678 | .{ | ||
| 679 | "@qualCast", | ||
| 680 | .{ | ||
| 681 | .tag = .qual_cast, | ||
| 682 | .param_count = 2, | ||
| 683 | }, | ||
| 684 | }, | ||
| 685 | .{ | 686 | .{ |
| 686 | "@rem", | 687 | "@rem", |
| 687 | .{ | 688 | .{ |
| ... | @@ -964,5 +965,12 @@ pub const list = list: { | ... | @@ -964,5 +965,12 @@ pub const list = list: { |
| 964 | .param_count = 2, | 965 | .param_count = 2, |
| 965 | }, | 966 | }, |
| 966 | }, | 967 | }, |
| 968 | .{ | ||
| 969 | "@volatileCast", | ||
| 970 | .{ | ||
| 971 | .tag = .volatile_cast, | ||
| 972 | .param_count = 1, | ||
| 973 | }, | ||
| 974 | }, | ||
| 967 | }); | 975 | }); |
| 968 | }; | 976 | }; |
src/Cache.zig deleted-1265| ... | @@ -1,1265 +0,0 @@ | ||
| 1 | //! Manages `zig-cache` directories. | ||
| 2 | //! This is not a general-purpose cache. It is designed to be fast and simple, | ||
| 3 | //! not to withstand attacks using specially-crafted input. | ||
| 4 | |||
| 5 | gpa: Allocator, | ||
| 6 | manifest_dir: fs.Dir, | ||
| 7 | hash: HashHelper = .{}, | ||
| 8 | /// This value is accessed from multiple threads, protected by mutex. | ||
| 9 | recent_problematic_timestamp: i128 = 0, | ||
| 10 | mutex: std.Thread.Mutex = .{}, | ||
| 11 | |||
| 12 | /// A set of strings such as the zig library directory or project source root, which | ||
| 13 | /// are stripped from the file paths before putting into the cache. They | ||
| 14 | /// are replaced with single-character indicators. This is not to save | ||
| 15 | /// space but to eliminate absolute file paths. This improves portability | ||
| 16 | /// and usefulness of the cache for advanced use cases. | ||
| 17 | prefixes_buffer: [3]Compilation.Directory = undefined, | ||
| 18 | prefixes_len: usize = 0, | ||
| 19 | |||
| 20 | const Cache = @This(); | ||
| 21 | const std = @import("std"); | ||
| 22 | const builtin = @import("builtin"); | ||
| 23 | const crypto = std.crypto; | ||
| 24 | const fs = std.fs; | ||
| 25 | const assert = std.debug.assert; | ||
| 26 | const testing = std.testing; | ||
| 27 | const mem = std.mem; | ||
| 28 | const fmt = std.fmt; | ||
| 29 | const Allocator = std.mem.Allocator; | ||
| 30 | const Compilation = @import("Compilation.zig"); | ||
| 31 | const log = std.log.scoped(.cache); | ||
| 32 | |||
| 33 | pub fn addPrefix(cache: *Cache, directory: Compilation.Directory) void { | ||
| 34 | if (directory.path) |p| { | ||
| 35 | log.debug("Cache.addPrefix {d} {s}", .{ cache.prefixes_len, p }); | ||
| 36 | } | ||
| 37 | cache.prefixes_buffer[cache.prefixes_len] = directory; | ||
| 38 | cache.prefixes_len += 1; | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Be sure to call `Manifest.deinit` after successful initialization. | ||
| 42 | pub fn obtain(cache: *Cache) Manifest { | ||
| 43 | return Manifest{ | ||
| 44 | .cache = cache, | ||
| 45 | .hash = cache.hash, | ||
| 46 | .manifest_file = null, | ||
| 47 | .manifest_dirty = false, | ||
| 48 | .hex_digest = undefined, | ||
| 49 | }; | ||
| 50 | } | ||
| 51 | |||
| 52 | pub fn prefixes(cache: *const Cache) []const Compilation.Directory { | ||
| 53 | return cache.prefixes_buffer[0..cache.prefixes_len]; | ||
| 54 | } | ||
| 55 | |||
| 56 | const PrefixedPath = struct { | ||
| 57 | prefix: u8, | ||
| 58 | sub_path: []u8, | ||
| 59 | }; | ||
| 60 | |||
| 61 | fn findPrefix(cache: *const Cache, file_path: []const u8) !PrefixedPath { | ||
| 62 | const gpa = cache.gpa; | ||
| 63 | const resolved_path = try fs.path.resolve(gpa, &[_][]const u8{file_path}); | ||
| 64 | errdefer gpa.free(resolved_path); | ||
| 65 | return findPrefixResolved(cache, resolved_path); | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Takes ownership of `resolved_path` on success. | ||
| 69 | fn findPrefixResolved(cache: *const Cache, resolved_path: []u8) !PrefixedPath { | ||
| 70 | const gpa = cache.gpa; | ||
| 71 | const prefixes_slice = cache.prefixes(); | ||
| 72 | var i: u8 = 1; // Start at 1 to skip over checking the null prefix. | ||
| 73 | while (i < prefixes_slice.len) : (i += 1) { | ||
| 74 | const p = prefixes_slice[i].path.?; | ||
| 75 | if (mem.startsWith(u8, resolved_path, p)) { | ||
| 76 | // +1 to skip over the path separator here | ||
| 77 | const sub_path = try gpa.dupe(u8, resolved_path[p.len + 1 ..]); | ||
| 78 | gpa.free(resolved_path); | ||
| 79 | return PrefixedPath{ | ||
| 80 | .prefix = @intCast(u8, i), | ||
| 81 | .sub_path = sub_path, | ||
| 82 | }; | ||
| 83 | } else { | ||
| 84 | log.debug("'{s}' does not start with '{s}'", .{ resolved_path, p }); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | return PrefixedPath{ | ||
| 89 | .prefix = 0, | ||
| 90 | .sub_path = resolved_path, | ||
| 91 | }; | ||
| 92 | } | ||
| 93 | |||
| 94 | /// This is 128 bits - Even with 2^54 cache entries, the probably of a collision would be under 10^-6 | ||
| 95 | pub const bin_digest_len = 16; | ||
| 96 | pub const hex_digest_len = bin_digest_len * 2; | ||
| 97 | pub const BinDigest = [bin_digest_len]u8; | ||
| 98 | |||
| 99 | const manifest_file_size_max = 50 * 1024 * 1024; | ||
| 100 | |||
| 101 | /// The type used for hashing file contents. Currently, this is SipHash128(1, 3), because it | ||
| 102 | /// provides enough collision resistance for the Manifest use cases, while being one of our | ||
| 103 | /// fastest options right now. | ||
| 104 | pub const Hasher = crypto.auth.siphash.SipHash128(1, 3); | ||
| 105 | |||
| 106 | /// Initial state, that can be copied. | ||
| 107 | pub const hasher_init: Hasher = Hasher.init(&[_]u8{0} ** Hasher.key_length); | ||
| 108 | |||
| 109 | pub const File = struct { | ||
| 110 | prefixed_path: ?PrefixedPath, | ||
| 111 | max_file_size: ?usize, | ||
| 112 | stat: Stat, | ||
| 113 | bin_digest: BinDigest, | ||
| 114 | contents: ?[]const u8, | ||
| 115 | |||
| 116 | pub const Stat = struct { | ||
| 117 | inode: fs.File.INode, | ||
| 118 | size: u64, | ||
| 119 | mtime: i128, | ||
| 120 | }; | ||
| 121 | |||
| 122 | pub fn deinit(self: *File, gpa: Allocator) void { | ||
| 123 | if (self.prefixed_path) |pp| { | ||
| 124 | gpa.free(pp.sub_path); | ||
| 125 | self.prefixed_path = null; | ||
| 126 | } | ||
| 127 | if (self.contents) |contents| { | ||
| 128 | gpa.free(contents); | ||
| 129 | self.contents = null; | ||
| 130 | } | ||
| 131 | self.* = undefined; | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | pub const HashHelper = struct { | ||
| 136 | hasher: Hasher = hasher_init, | ||
| 137 | |||
| 138 | const EmitLoc = Compilation.EmitLoc; | ||
| 139 | |||
| 140 | /// Record a slice of bytes as an dependency of the process being cached | ||
| 141 | pub fn addBytes(hh: *HashHelper, bytes: []const u8) void { | ||
| 142 | hh.hasher.update(mem.asBytes(&bytes.len)); | ||
| 143 | hh.hasher.update(bytes); | ||
| 144 | } | ||
| 145 | |||
| 146 | pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void { | ||
| 147 | hh.add(optional_bytes != null); | ||
| 148 | hh.addBytes(optional_bytes orelse return); | ||
| 149 | } | ||
| 150 | |||
| 151 | pub fn addEmitLoc(hh: *HashHelper, emit_loc: EmitLoc) void { | ||
| 152 | hh.addBytes(emit_loc.basename); | ||
| 153 | } | ||
| 154 | |||
| 155 | pub fn addOptionalEmitLoc(hh: *HashHelper, optional_emit_loc: ?EmitLoc) void { | ||
| 156 | hh.add(optional_emit_loc != null); | ||
| 157 | hh.addEmitLoc(optional_emit_loc orelse return); | ||
| 158 | } | ||
| 159 | |||
| 160 | pub fn addListOfBytes(hh: *HashHelper, list_of_bytes: []const []const u8) void { | ||
| 161 | hh.add(list_of_bytes.len); | ||
| 162 | for (list_of_bytes) |bytes| hh.addBytes(bytes); | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Convert the input value into bytes and record it as a dependency of the process being cached. | ||
| 166 | pub fn add(hh: *HashHelper, x: anytype) void { | ||
| 167 | switch (@TypeOf(x)) { | ||
| 168 | std.builtin.Version => { | ||
| 169 | hh.add(x.major); | ||
| 170 | hh.add(x.minor); | ||
| 171 | hh.add(x.patch); | ||
| 172 | }, | ||
| 173 | std.Target.Os.TaggedVersionRange => { | ||
| 174 | switch (x) { | ||
| 175 | .linux => |linux| { | ||
| 176 | hh.add(linux.range.min); | ||
| 177 | hh.add(linux.range.max); | ||
| 178 | hh.add(linux.glibc); | ||
| 179 | }, | ||
| 180 | .windows => |windows| { | ||
| 181 | hh.add(windows.min); | ||
| 182 | hh.add(windows.max); | ||
| 183 | }, | ||
| 184 | .semver => |semver| { | ||
| 185 | hh.add(semver.min); | ||
| 186 | hh.add(semver.max); | ||
| 187 | }, | ||
| 188 | .none => {}, | ||
| 189 | } | ||
| 190 | }, | ||
| 191 | else => switch (@typeInfo(@TypeOf(x))) { | ||
| 192 | .Bool, .Int, .Enum, .Array => hh.addBytes(mem.asBytes(&x)), | ||
| 193 | else => @compileError("unable to hash type " ++ @typeName(@TypeOf(x))), | ||
| 194 | }, | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | pub fn addOptional(hh: *HashHelper, optional: anytype) void { | ||
| 199 | hh.add(optional != null); | ||
| 200 | hh.add(optional orelse return); | ||
| 201 | } | ||
| 202 | |||
| 203 | /// Returns a hex encoded hash of the inputs, without modifying state. | ||
| 204 | pub fn peek(hh: HashHelper) [hex_digest_len]u8 { | ||
| 205 | var copy = hh; | ||
| 206 | return copy.final(); | ||
| 207 | } | ||
| 208 | |||
| 209 | pub fn peekBin(hh: HashHelper) BinDigest { | ||
| 210 | var copy = hh; | ||
| 211 | var bin_digest: BinDigest = undefined; | ||
| 212 | copy.hasher.final(&bin_digest); | ||
| 213 | return bin_digest; | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Returns a hex encoded hash of the inputs, mutating the state of the hasher. | ||
| 217 | pub fn final(hh: *HashHelper) [hex_digest_len]u8 { | ||
| 218 | var bin_digest: BinDigest = undefined; | ||
| 219 | hh.hasher.final(&bin_digest); | ||
| 220 | |||
| 221 | var out_digest: [hex_digest_len]u8 = undefined; | ||
| 222 | _ = std.fmt.bufPrint( | ||
| 223 | &out_digest, | ||
| 224 | "{s}", | ||
| 225 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | ||
| 226 | ) catch unreachable; | ||
| 227 | return out_digest; | ||
| 228 | } | ||
| 229 | }; | ||
| 230 | |||
| 231 | pub const Lock = struct { | ||
| 232 | manifest_file: fs.File, | ||
| 233 | |||
| 234 | pub fn release(lock: *Lock) void { | ||
| 235 | if (builtin.os.tag == .windows) { | ||
| 236 | // Windows does not guarantee that locks are immediately unlocked when | ||
| 237 | // the file handle is closed. See LockFileEx documentation. | ||
| 238 | lock.manifest_file.unlock(); | ||
| 239 | } | ||
| 240 | |||
| 241 | lock.manifest_file.close(); | ||
| 242 | lock.* = undefined; | ||
| 243 | } | ||
| 244 | }; | ||
| 245 | |||
| 246 | pub const Manifest = struct { | ||
| 247 | cache: *Cache, | ||
| 248 | /// Current state for incremental hashing. | ||
| 249 | hash: HashHelper, | ||
| 250 | manifest_file: ?fs.File, | ||
| 251 | manifest_dirty: bool, | ||
| 252 | /// Set this flag to true before calling hit() in order to indicate that | ||
| 253 | /// upon a cache hit, the code using the cache will not modify the files | ||
| 254 | /// within the cache directory. This allows multiple processes to utilize | ||
| 255 | /// the same cache directory at the same time. | ||
| 256 | want_shared_lock: bool = true, | ||
| 257 | have_exclusive_lock: bool = false, | ||
| 258 | // Indicate that we want isProblematicTimestamp to perform a filesystem write in | ||
| 259 | // order to obtain a problematic timestamp for the next call. Calls after that | ||
| 260 | // will then use the same timestamp, to avoid unnecessary filesystem writes. | ||
| 261 | want_refresh_timestamp: bool = true, | ||
| 262 | files: std.ArrayListUnmanaged(File) = .{}, | ||
| 263 | hex_digest: [hex_digest_len]u8, | ||
| 264 | /// Populated when hit() returns an error because of one | ||
| 265 | /// of the files listed in the manifest. | ||
| 266 | failed_file_index: ?usize = null, | ||
| 267 | /// Keeps track of the last time we performed a file system write to observe | ||
| 268 | /// what time the file system thinks it is, according to its own granularity. | ||
| 269 | recent_problematic_timestamp: i128 = 0, | ||
| 270 | |||
| 271 | /// Add a file as a dependency of process being cached. When `hit` is | ||
| 272 | /// called, the file's contents will be checked to ensure that it matches | ||
| 273 | /// the contents from previous times. | ||
| 274 | /// | ||
| 275 | /// Max file size will be used to determine the amount of space the file contents | ||
| 276 | /// are allowed to take up in memory. If max_file_size is null, then the contents | ||
| 277 | /// will not be loaded into memory. | ||
| 278 | /// | ||
| 279 | /// Returns the index of the entry in the `files` array list. You can use it | ||
| 280 | /// to access the contents of the file after calling `hit()` like so: | ||
| 281 | /// | ||
| 282 | /// ``` | ||
| 283 | /// var file_contents = cache_hash.files.items[file_index].contents.?; | ||
| 284 | /// ``` | ||
| 285 | pub fn addFile(self: *Manifest, file_path: []const u8, max_file_size: ?usize) !usize { | ||
| 286 | assert(self.manifest_file == null); | ||
| 287 | |||
| 288 | const gpa = self.cache.gpa; | ||
| 289 | try self.files.ensureUnusedCapacity(gpa, 1); | ||
| 290 | const prefixed_path = try self.cache.findPrefix(file_path); | ||
| 291 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 292 | |||
| 293 | log.debug("Manifest.addFile {s} -> {d} {s}", .{ | ||
| 294 | file_path, prefixed_path.prefix, prefixed_path.sub_path, | ||
| 295 | }); | ||
| 296 | |||
| 297 | self.files.addOneAssumeCapacity().* = .{ | ||
| 298 | .prefixed_path = prefixed_path, | ||
| 299 | .contents = null, | ||
| 300 | .max_file_size = max_file_size, | ||
| 301 | .stat = undefined, | ||
| 302 | .bin_digest = undefined, | ||
| 303 | }; | ||
| 304 | |||
| 305 | self.hash.add(prefixed_path.prefix); | ||
| 306 | self.hash.addBytes(prefixed_path.sub_path); | ||
| 307 | |||
| 308 | return self.files.items.len - 1; | ||
| 309 | } | ||
| 310 | |||
| 311 | pub fn hashCSource(self: *Manifest, c_source: Compilation.CSourceFile) !void { | ||
| 312 | _ = try self.addFile(c_source.src_path, null); | ||
| 313 | // Hash the extra flags, with special care to call addFile for file parameters. | ||
| 314 | // TODO this logic can likely be improved by utilizing clang_options_data.zig. | ||
| 315 | const file_args = [_][]const u8{"-include"}; | ||
| 316 | var arg_i: usize = 0; | ||
| 317 | while (arg_i < c_source.extra_flags.len) : (arg_i += 1) { | ||
| 318 | const arg = c_source.extra_flags[arg_i]; | ||
| 319 | self.hash.addBytes(arg); | ||
| 320 | for (file_args) |file_arg| { | ||
| 321 | if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) { | ||
| 322 | arg_i += 1; | ||
| 323 | _ = try self.addFile(c_source.extra_flags[arg_i], null); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | pub fn addOptionalFile(self: *Manifest, optional_file_path: ?[]const u8) !void { | ||
| 330 | self.hash.add(optional_file_path != null); | ||
| 331 | const file_path = optional_file_path orelse return; | ||
| 332 | _ = try self.addFile(file_path, null); | ||
| 333 | } | ||
| 334 | |||
| 335 | pub fn addListOfFiles(self: *Manifest, list_of_files: []const []const u8) !void { | ||
| 336 | self.hash.add(list_of_files.len); | ||
| 337 | for (list_of_files) |file_path| { | ||
| 338 | _ = try self.addFile(file_path, null); | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | /// Check the cache to see if the input exists in it. If it exists, returns `true`. | ||
| 343 | /// A hex encoding of its hash is available by calling `final`. | ||
| 344 | /// | ||
| 345 | /// This function will also acquire an exclusive lock to the manifest file. This means | ||
| 346 | /// that a process holding a Manifest will block any other process attempting to | ||
| 347 | /// acquire the lock. If `want_shared_lock` is `true`, a cache hit guarantees the | ||
| 348 | /// manifest file to be locked in shared mode, and a cache miss guarantees the manifest | ||
| 349 | /// file to be locked in exclusive mode. | ||
| 350 | /// | ||
| 351 | /// The lock on the manifest file is released when `deinit` is called. As another | ||
| 352 | /// option, one may call `toOwnedLock` to obtain a smaller object which can represent | ||
| 353 | /// the lock. `deinit` is safe to call whether or not `toOwnedLock` has been called. | ||
| 354 | pub fn hit(self: *Manifest) !bool { | ||
| 355 | const gpa = self.cache.gpa; | ||
| 356 | assert(self.manifest_file == null); | ||
| 357 | |||
| 358 | self.failed_file_index = null; | ||
| 359 | |||
| 360 | const ext = ".txt"; | ||
| 361 | var manifest_file_path: [self.hex_digest.len + ext.len]u8 = undefined; | ||
| 362 | |||
| 363 | var bin_digest: BinDigest = undefined; | ||
| 364 | self.hash.hasher.final(&bin_digest); | ||
| 365 | |||
| 366 | _ = std.fmt.bufPrint( | ||
| 367 | &self.hex_digest, | ||
| 368 | "{s}", | ||
| 369 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | ||
| 370 | ) catch unreachable; | ||
| 371 | |||
| 372 | self.hash.hasher = hasher_init; | ||
| 373 | self.hash.hasher.update(&bin_digest); | ||
| 374 | |||
| 375 | mem.copy(u8, &manifest_file_path, &self.hex_digest); | ||
| 376 | manifest_file_path[self.hex_digest.len..][0..ext.len].* = ext.*; | ||
| 377 | |||
| 378 | if (self.files.items.len == 0) { | ||
| 379 | // If there are no file inputs, we check if the manifest file exists instead of | ||
| 380 | // comparing the hashes on the files used for the cached item | ||
| 381 | while (true) { | ||
| 382 | if (self.cache.manifest_dir.openFile(&manifest_file_path, .{ | ||
| 383 | .mode = .read_write, | ||
| 384 | .lock = .Exclusive, | ||
| 385 | .lock_nonblocking = self.want_shared_lock, | ||
| 386 | })) |manifest_file| { | ||
| 387 | self.manifest_file = manifest_file; | ||
| 388 | self.have_exclusive_lock = true; | ||
| 389 | break; | ||
| 390 | } else |open_err| switch (open_err) { | ||
| 391 | error.WouldBlock => { | ||
| 392 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | ||
| 393 | .lock = .Shared, | ||
| 394 | }); | ||
| 395 | break; | ||
| 396 | }, | ||
| 397 | error.FileNotFound => { | ||
| 398 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ | ||
| 399 | .read = true, | ||
| 400 | .truncate = false, | ||
| 401 | .lock = .Exclusive, | ||
| 402 | .lock_nonblocking = self.want_shared_lock, | ||
| 403 | })) |manifest_file| { | ||
| 404 | self.manifest_file = manifest_file; | ||
| 405 | self.manifest_dirty = true; | ||
| 406 | self.have_exclusive_lock = true; | ||
| 407 | return false; // cache miss; exclusive lock already held | ||
| 408 | } else |err| switch (err) { | ||
| 409 | error.WouldBlock => continue, | ||
| 410 | else => |e| return e, | ||
| 411 | } | ||
| 412 | }, | ||
| 413 | else => |e| return e, | ||
| 414 | } | ||
| 415 | } | ||
| 416 | } else { | ||
| 417 | if (self.cache.manifest_dir.createFile(&manifest_file_path, .{ | ||
| 418 | .read = true, | ||
| 419 | .truncate = false, | ||
| 420 | .lock = .Exclusive, | ||
| 421 | .lock_nonblocking = self.want_shared_lock, | ||
| 422 | })) |manifest_file| { | ||
| 423 | self.manifest_file = manifest_file; | ||
| 424 | self.have_exclusive_lock = true; | ||
| 425 | } else |err| switch (err) { | ||
| 426 | error.WouldBlock => { | ||
| 427 | self.manifest_file = try self.cache.manifest_dir.openFile(&manifest_file_path, .{ | ||
| 428 | .lock = .Shared, | ||
| 429 | }); | ||
| 430 | }, | ||
| 431 | else => |e| return e, | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | self.want_refresh_timestamp = true; | ||
| 436 | |||
| 437 | const file_contents = try self.manifest_file.?.reader().readAllAlloc(gpa, manifest_file_size_max); | ||
| 438 | defer gpa.free(file_contents); | ||
| 439 | |||
| 440 | const input_file_count = self.files.items.len; | ||
| 441 | var any_file_changed = false; | ||
| 442 | var line_iter = mem.tokenize(u8, file_contents, "\n"); | ||
| 443 | var idx: usize = 0; | ||
| 444 | while (line_iter.next()) |line| { | ||
| 445 | defer idx += 1; | ||
| 446 | |||
| 447 | const cache_hash_file = if (idx < input_file_count) &self.files.items[idx] else blk: { | ||
| 448 | const new = try self.files.addOne(gpa); | ||
| 449 | new.* = .{ | ||
| 450 | .prefixed_path = null, | ||
| 451 | .contents = null, | ||
| 452 | .max_file_size = null, | ||
| 453 | .stat = undefined, | ||
| 454 | .bin_digest = undefined, | ||
| 455 | }; | ||
| 456 | break :blk new; | ||
| 457 | }; | ||
| 458 | |||
| 459 | var iter = mem.tokenize(u8, line, " "); | ||
| 460 | const size = iter.next() orelse return error.InvalidFormat; | ||
| 461 | const inode = iter.next() orelse return error.InvalidFormat; | ||
| 462 | const mtime_nsec_str = iter.next() orelse return error.InvalidFormat; | ||
| 463 | const digest_str = iter.next() orelse return error.InvalidFormat; | ||
| 464 | const prefix_str = iter.next() orelse return error.InvalidFormat; | ||
| 465 | const file_path = iter.rest(); | ||
| 466 | |||
| 467 | cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; | ||
| 468 | cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; | ||
| 469 | cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; | ||
| 470 | _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; | ||
| 471 | const prefix = fmt.parseInt(u8, prefix_str, 10) catch return error.InvalidFormat; | ||
| 472 | if (prefix >= self.cache.prefixes_len) return error.InvalidFormat; | ||
| 473 | |||
| 474 | if (file_path.len == 0) { | ||
| 475 | return error.InvalidFormat; | ||
| 476 | } | ||
| 477 | if (cache_hash_file.prefixed_path) |pp| { | ||
| 478 | if (pp.prefix != prefix or !mem.eql(u8, file_path, pp.sub_path)) { | ||
| 479 | return error.InvalidFormat; | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | if (cache_hash_file.prefixed_path == null) { | ||
| 484 | cache_hash_file.prefixed_path = .{ | ||
| 485 | .prefix = prefix, | ||
| 486 | .sub_path = try gpa.dupe(u8, file_path), | ||
| 487 | }; | ||
| 488 | } | ||
| 489 | |||
| 490 | const pp = cache_hash_file.prefixed_path.?; | ||
| 491 | const dir = self.cache.prefixes()[pp.prefix].handle; | ||
| 492 | const this_file = dir.openFile(pp.sub_path, .{ .mode = .read_only }) catch |err| switch (err) { | ||
| 493 | error.FileNotFound => { | ||
| 494 | try self.upgradeToExclusiveLock(); | ||
| 495 | return false; | ||
| 496 | }, | ||
| 497 | else => return error.CacheUnavailable, | ||
| 498 | }; | ||
| 499 | defer this_file.close(); | ||
| 500 | |||
| 501 | const actual_stat = this_file.stat() catch |err| { | ||
| 502 | self.failed_file_index = idx; | ||
| 503 | return err; | ||
| 504 | }; | ||
| 505 | const size_match = actual_stat.size == cache_hash_file.stat.size; | ||
| 506 | const mtime_match = actual_stat.mtime == cache_hash_file.stat.mtime; | ||
| 507 | const inode_match = actual_stat.inode == cache_hash_file.stat.inode; | ||
| 508 | |||
| 509 | if (!size_match or !mtime_match or !inode_match) { | ||
| 510 | self.manifest_dirty = true; | ||
| 511 | |||
| 512 | cache_hash_file.stat = .{ | ||
| 513 | .size = actual_stat.size, | ||
| 514 | .mtime = actual_stat.mtime, | ||
| 515 | .inode = actual_stat.inode, | ||
| 516 | }; | ||
| 517 | |||
| 518 | if (self.isProblematicTimestamp(cache_hash_file.stat.mtime)) { | ||
| 519 | // The actual file has an unreliable timestamp, force it to be hashed | ||
| 520 | cache_hash_file.stat.mtime = 0; | ||
| 521 | cache_hash_file.stat.inode = 0; | ||
| 522 | } | ||
| 523 | |||
| 524 | var actual_digest: BinDigest = undefined; | ||
| 525 | hashFile(this_file, &actual_digest) catch |err| { | ||
| 526 | self.failed_file_index = idx; | ||
| 527 | return err; | ||
| 528 | }; | ||
| 529 | |||
| 530 | if (!mem.eql(u8, &cache_hash_file.bin_digest, &actual_digest)) { | ||
| 531 | cache_hash_file.bin_digest = actual_digest; | ||
| 532 | // keep going until we have the input file digests | ||
| 533 | any_file_changed = true; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | if (!any_file_changed) { | ||
| 538 | self.hash.hasher.update(&cache_hash_file.bin_digest); | ||
| 539 | } | ||
| 540 | } | ||
| 541 | |||
| 542 | if (any_file_changed) { | ||
| 543 | // cache miss | ||
| 544 | // keep the manifest file open | ||
| 545 | self.unhit(bin_digest, input_file_count); | ||
| 546 | try self.upgradeToExclusiveLock(); | ||
| 547 | return false; | ||
| 548 | } | ||
| 549 | |||
| 550 | if (idx < input_file_count) { | ||
| 551 | self.manifest_dirty = true; | ||
| 552 | while (idx < input_file_count) : (idx += 1) { | ||
| 553 | const ch_file = &self.files.items[idx]; | ||
| 554 | self.populateFileHash(ch_file) catch |err| { | ||
| 555 | self.failed_file_index = idx; | ||
| 556 | return err; | ||
| 557 | }; | ||
| 558 | } | ||
| 559 | try self.upgradeToExclusiveLock(); | ||
| 560 | return false; | ||
| 561 | } | ||
| 562 | |||
| 563 | if (self.want_shared_lock) { | ||
| 564 | try self.downgradeToSharedLock(); | ||
| 565 | } | ||
| 566 | |||
| 567 | return true; | ||
| 568 | } | ||
| 569 | |||
| 570 | pub fn unhit(self: *Manifest, bin_digest: BinDigest, input_file_count: usize) void { | ||
| 571 | // Reset the hash. | ||
| 572 | self.hash.hasher = hasher_init; | ||
| 573 | self.hash.hasher.update(&bin_digest); | ||
| 574 | |||
| 575 | // Remove files not in the initial hash. | ||
| 576 | for (self.files.items[input_file_count..]) |*file| { | ||
| 577 | file.deinit(self.cache.gpa); | ||
| 578 | } | ||
| 579 | self.files.shrinkRetainingCapacity(input_file_count); | ||
| 580 | |||
| 581 | for (self.files.items) |file| { | ||
| 582 | self.hash.hasher.update(&file.bin_digest); | ||
| 583 | } | ||
| 584 | } | ||
| 585 | |||
| 586 | fn isProblematicTimestamp(man: *Manifest, file_time: i128) bool { | ||
| 587 | // If the file_time is prior to the most recent problematic timestamp | ||
| 588 | // then we don't need to access the filesystem. | ||
| 589 | if (file_time < man.recent_problematic_timestamp) | ||
| 590 | return false; | ||
| 591 | |||
| 592 | // Next we will check the globally shared Cache timestamp, which is accessed | ||
| 593 | // from multiple threads. | ||
| 594 | man.cache.mutex.lock(); | ||
| 595 | defer man.cache.mutex.unlock(); | ||
| 596 | |||
| 597 | // Save the global one to our local one to avoid locking next time. | ||
| 598 | man.recent_problematic_timestamp = man.cache.recent_problematic_timestamp; | ||
| 599 | if (file_time < man.recent_problematic_timestamp) | ||
| 600 | return false; | ||
| 601 | |||
| 602 | // This flag prevents multiple filesystem writes for the same hit() call. | ||
| 603 | if (man.want_refresh_timestamp) { | ||
| 604 | man.want_refresh_timestamp = false; | ||
| 605 | |||
| 606 | var file = man.cache.manifest_dir.createFile("timestamp", .{ | ||
| 607 | .read = true, | ||
| 608 | .truncate = true, | ||
| 609 | }) catch return true; | ||
| 610 | defer file.close(); | ||
| 611 | |||
| 612 | // Save locally and also save globally (we still hold the global lock). | ||
| 613 | man.recent_problematic_timestamp = (file.stat() catch return true).mtime; | ||
| 614 | man.cache.recent_problematic_timestamp = man.recent_problematic_timestamp; | ||
| 615 | } | ||
| 616 | |||
| 617 | return file_time >= man.recent_problematic_timestamp; | ||
| 618 | } | ||
| 619 | |||
| 620 | fn populateFileHash(self: *Manifest, ch_file: *File) !void { | ||
| 621 | const pp = ch_file.prefixed_path.?; | ||
| 622 | const dir = self.cache.prefixes()[pp.prefix].handle; | ||
| 623 | const file = try dir.openFile(pp.sub_path, .{}); | ||
| 624 | defer file.close(); | ||
| 625 | |||
| 626 | const actual_stat = try file.stat(); | ||
| 627 | ch_file.stat = .{ | ||
| 628 | .size = actual_stat.size, | ||
| 629 | .mtime = actual_stat.mtime, | ||
| 630 | .inode = actual_stat.inode, | ||
| 631 | }; | ||
| 632 | |||
| 633 | if (self.isProblematicTimestamp(ch_file.stat.mtime)) { | ||
| 634 | // The actual file has an unreliable timestamp, force it to be hashed | ||
| 635 | ch_file.stat.mtime = 0; | ||
| 636 | ch_file.stat.inode = 0; | ||
| 637 | } | ||
| 638 | |||
| 639 | if (ch_file.max_file_size) |max_file_size| { | ||
| 640 | if (ch_file.stat.size > max_file_size) { | ||
| 641 | return error.FileTooBig; | ||
| 642 | } | ||
| 643 | |||
| 644 | const contents = try self.cache.gpa.alloc(u8, @intCast(usize, ch_file.stat.size)); | ||
| 645 | errdefer self.cache.gpa.free(contents); | ||
| 646 | |||
| 647 | // Hash while reading from disk, to keep the contents in the cpu cache while | ||
| 648 | // doing hashing. | ||
| 649 | var hasher = hasher_init; | ||
| 650 | var off: usize = 0; | ||
| 651 | while (true) { | ||
| 652 | // give me everything you've got, captain | ||
| 653 | const bytes_read = try file.read(contents[off..]); | ||
| 654 | if (bytes_read == 0) break; | ||
| 655 | hasher.update(contents[off..][0..bytes_read]); | ||
| 656 | off += bytes_read; | ||
| 657 | } | ||
| 658 | hasher.final(&ch_file.bin_digest); | ||
| 659 | |||
| 660 | ch_file.contents = contents; | ||
| 661 | } else { | ||
| 662 | try hashFile(file, &ch_file.bin_digest); | ||
| 663 | } | ||
| 664 | |||
| 665 | self.hash.hasher.update(&ch_file.bin_digest); | ||
| 666 | } | ||
| 667 | |||
| 668 | /// Add a file as a dependency of process being cached, after the initial hash has been | ||
| 669 | /// calculated. This is useful for processes that don't know all the files that | ||
| 670 | /// are depended on ahead of time. For example, a source file that can import other files | ||
| 671 | /// will need to be recompiled if the imported file is changed. | ||
| 672 | pub fn addFilePostFetch(self: *Manifest, file_path: []const u8, max_file_size: usize) ![]const u8 { | ||
| 673 | assert(self.manifest_file != null); | ||
| 674 | |||
| 675 | const gpa = self.cache.gpa; | ||
| 676 | const prefixed_path = try self.cache.findPrefix(file_path); | ||
| 677 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 678 | |||
| 679 | log.debug("Manifest.addFilePostFetch {s} -> {d} {s}", .{ | ||
| 680 | file_path, prefixed_path.prefix, prefixed_path.sub_path, | ||
| 681 | }); | ||
| 682 | |||
| 683 | const new_ch_file = try self.files.addOne(gpa); | ||
| 684 | new_ch_file.* = .{ | ||
| 685 | .prefixed_path = prefixed_path, | ||
| 686 | .max_file_size = max_file_size, | ||
| 687 | .stat = undefined, | ||
| 688 | .bin_digest = undefined, | ||
| 689 | .contents = null, | ||
| 690 | }; | ||
| 691 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); | ||
| 692 | |||
| 693 | try self.populateFileHash(new_ch_file); | ||
| 694 | |||
| 695 | return new_ch_file.contents.?; | ||
| 696 | } | ||
| 697 | |||
| 698 | /// Add a file as a dependency of process being cached, after the initial hash has been | ||
| 699 | /// calculated. This is useful for processes that don't know the all the files that | ||
| 700 | /// are depended on ahead of time. For example, a source file that can import other files | ||
| 701 | /// will need to be recompiled if the imported file is changed. | ||
| 702 | pub fn addFilePost(self: *Manifest, file_path: []const u8) !void { | ||
| 703 | assert(self.manifest_file != null); | ||
| 704 | |||
| 705 | const gpa = self.cache.gpa; | ||
| 706 | const prefixed_path = try self.cache.findPrefix(file_path); | ||
| 707 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 708 | |||
| 709 | log.debug("Manifest.addFilePost {s} -> {d} {s}", .{ | ||
| 710 | file_path, prefixed_path.prefix, prefixed_path.sub_path, | ||
| 711 | }); | ||
| 712 | |||
| 713 | const new_ch_file = try self.files.addOne(gpa); | ||
| 714 | new_ch_file.* = .{ | ||
| 715 | .prefixed_path = prefixed_path, | ||
| 716 | .max_file_size = null, | ||
| 717 | .stat = undefined, | ||
| 718 | .bin_digest = undefined, | ||
| 719 | .contents = null, | ||
| 720 | }; | ||
| 721 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); | ||
| 722 | |||
| 723 | try self.populateFileHash(new_ch_file); | ||
| 724 | } | ||
| 725 | |||
| 726 | /// Like `addFilePost` but when the file contents have already been loaded from disk. | ||
| 727 | /// On success, cache takes ownership of `resolved_path`. | ||
| 728 | pub fn addFilePostContents( | ||
| 729 | self: *Manifest, | ||
| 730 | resolved_path: []u8, | ||
| 731 | bytes: []const u8, | ||
| 732 | stat: File.Stat, | ||
| 733 | ) error{OutOfMemory}!void { | ||
| 734 | assert(self.manifest_file != null); | ||
| 735 | const gpa = self.cache.gpa; | ||
| 736 | |||
| 737 | const ch_file = try self.files.addOne(gpa); | ||
| 738 | errdefer self.files.shrinkRetainingCapacity(self.files.items.len - 1); | ||
| 739 | |||
| 740 | log.debug("Manifest.addFilePostContents resolved_path={s}", .{resolved_path}); | ||
| 741 | |||
| 742 | const prefixed_path = try self.cache.findPrefixResolved(resolved_path); | ||
| 743 | errdefer gpa.free(prefixed_path.sub_path); | ||
| 744 | |||
| 745 | log.debug("Manifest.addFilePostContents -> {d} {s}", .{ | ||
| 746 | prefixed_path.prefix, prefixed_path.sub_path, | ||
| 747 | }); | ||
| 748 | |||
| 749 | ch_file.* = .{ | ||
| 750 | .prefixed_path = prefixed_path, | ||
| 751 | .max_file_size = null, | ||
| 752 | .stat = stat, | ||
| 753 | .bin_digest = undefined, | ||
| 754 | .contents = null, | ||
| 755 | }; | ||
| 756 | |||
| 757 | if (self.isProblematicTimestamp(ch_file.stat.mtime)) { | ||
| 758 | // The actual file has an unreliable timestamp, force it to be hashed | ||
| 759 | ch_file.stat.mtime = 0; | ||
| 760 | ch_file.stat.inode = 0; | ||
| 761 | } | ||
| 762 | |||
| 763 | { | ||
| 764 | var hasher = hasher_init; | ||
| 765 | hasher.update(bytes); | ||
| 766 | hasher.final(&ch_file.bin_digest); | ||
| 767 | } | ||
| 768 | |||
| 769 | self.hash.hasher.update(&ch_file.bin_digest); | ||
| 770 | } | ||
| 771 | |||
| 772 | pub fn addDepFilePost(self: *Manifest, dir: fs.Dir, dep_file_basename: []const u8) !void { | ||
| 773 | assert(self.manifest_file != null); | ||
| 774 | |||
| 775 | const dep_file_contents = try dir.readFileAlloc(self.cache.gpa, dep_file_basename, manifest_file_size_max); | ||
| 776 | defer self.cache.gpa.free(dep_file_contents); | ||
| 777 | |||
| 778 | var error_buf = std.ArrayList(u8).init(self.cache.gpa); | ||
| 779 | defer error_buf.deinit(); | ||
| 780 | |||
| 781 | var it: @import("DepTokenizer.zig") = .{ .bytes = dep_file_contents }; | ||
| 782 | |||
| 783 | // Skip first token: target. | ||
| 784 | switch (it.next() orelse return) { // Empty dep file OK. | ||
| 785 | .target, .target_must_resolve, .prereq => {}, | ||
| 786 | else => |err| { | ||
| 787 | try err.printError(error_buf.writer()); | ||
| 788 | log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); | ||
| 789 | return error.InvalidDepFile; | ||
| 790 | }, | ||
| 791 | } | ||
| 792 | // Process 0+ preqreqs. | ||
| 793 | // Clang is invoked in single-source mode so we never get more targets. | ||
| 794 | while (true) { | ||
| 795 | switch (it.next() orelse return) { | ||
| 796 | .target, .target_must_resolve => return, | ||
| 797 | .prereq => |file_path| try self.addFilePost(file_path), | ||
| 798 | else => |err| { | ||
| 799 | try err.printError(error_buf.writer()); | ||
| 800 | log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); | ||
| 801 | return error.InvalidDepFile; | ||
| 802 | }, | ||
| 803 | } | ||
| 804 | } | ||
| 805 | } | ||
| 806 | |||
| 807 | /// Returns a hex encoded hash of the inputs. | ||
| 808 | pub fn final(self: *Manifest) [hex_digest_len]u8 { | ||
| 809 | assert(self.manifest_file != null); | ||
| 810 | |||
| 811 | // We don't close the manifest file yet, because we want to | ||
| 812 | // keep it locked until the API user is done using it. | ||
| 813 | // We also don't write out the manifest yet, because until | ||
| 814 | // cache_release is called we still might be working on creating | ||
| 815 | // the artifacts to cache. | ||
| 816 | |||
| 817 | var bin_digest: BinDigest = undefined; | ||
| 818 | self.hash.hasher.final(&bin_digest); | ||
| 819 | |||
| 820 | var out_digest: [hex_digest_len]u8 = undefined; | ||
| 821 | _ = std.fmt.bufPrint( | ||
| 822 | &out_digest, | ||
| 823 | "{s}", | ||
| 824 | .{std.fmt.fmtSliceHexLower(&bin_digest)}, | ||
| 825 | ) catch unreachable; | ||
| 826 | |||
| 827 | return out_digest; | ||
| 828 | } | ||
| 829 | |||
| 830 | /// If `want_shared_lock` is true, this function automatically downgrades the | ||
| 831 | /// lock from exclusive to shared. | ||
| 832 | pub fn writeManifest(self: *Manifest) !void { | ||
| 833 | assert(self.have_exclusive_lock); | ||
| 834 | |||
| 835 | const manifest_file = self.manifest_file.?; | ||
| 836 | if (self.manifest_dirty) { | ||
| 837 | self.manifest_dirty = false; | ||
| 838 | |||
| 839 | var contents = std.ArrayList(u8).init(self.cache.gpa); | ||
| 840 | defer contents.deinit(); | ||
| 841 | |||
| 842 | const writer = contents.writer(); | ||
| 843 | var encoded_digest: [hex_digest_len]u8 = undefined; | ||
| 844 | |||
| 845 | for (self.files.items) |file| { | ||
| 846 | _ = std.fmt.bufPrint( | ||
| 847 | &encoded_digest, | ||
| 848 | "{s}", | ||
| 849 | .{std.fmt.fmtSliceHexLower(&file.bin_digest)}, | ||
| 850 | ) catch unreachable; | ||
| 851 | try writer.print("{d} {d} {d} {s} {d} {s}\n", .{ | ||
| 852 | file.stat.size, | ||
| 853 | file.stat.inode, | ||
| 854 | file.stat.mtime, | ||
| 855 | &encoded_digest, | ||
| 856 | file.prefixed_path.?.prefix, | ||
| 857 | file.prefixed_path.?.sub_path, | ||
| 858 | }); | ||
| 859 | } | ||
| 860 | |||
| 861 | try manifest_file.setEndPos(contents.items.len); | ||
| 862 | try manifest_file.pwriteAll(contents.items, 0); | ||
| 863 | } | ||
| 864 | |||
| 865 | if (self.want_shared_lock) { | ||
| 866 | try self.downgradeToSharedLock(); | ||
| 867 | } | ||
| 868 | } | ||
| 869 | |||
| 870 | fn downgradeToSharedLock(self: *Manifest) !void { | ||
| 871 | if (!self.have_exclusive_lock) return; | ||
| 872 | |||
| 873 | // WASI does not currently support flock, so we bypass it here. | ||
| 874 | // TODO: If/when flock is supported on WASI, this check should be removed. | ||
| 875 | // See https://github.com/WebAssembly/wasi-filesystem/issues/2 | ||
| 876 | if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) { | ||
| 877 | const manifest_file = self.manifest_file.?; | ||
| 878 | try manifest_file.downgradeLock(); | ||
| 879 | } | ||
| 880 | |||
| 881 | self.have_exclusive_lock = false; | ||
| 882 | } | ||
| 883 | |||
| 884 | fn upgradeToExclusiveLock(self: *Manifest) !void { | ||
| 885 | if (self.have_exclusive_lock) return; | ||
| 886 | assert(self.manifest_file != null); | ||
| 887 | |||
| 888 | // WASI does not currently support flock, so we bypass it here. | ||
| 889 | // TODO: If/when flock is supported on WASI, this check should be removed. | ||
| 890 | // See https://github.com/WebAssembly/wasi-filesystem/issues/2 | ||
| 891 | if (builtin.os.tag != .wasi or std.process.can_spawn or !builtin.single_threaded) { | ||
| 892 | const manifest_file = self.manifest_file.?; | ||
| 893 | // Here we intentionally have a period where the lock is released, in case there are | ||
| 894 | // other processes holding a shared lock. | ||
| 895 | manifest_file.unlock(); | ||
| 896 | try manifest_file.lock(.Exclusive); | ||
| 897 | } | ||
| 898 | self.have_exclusive_lock = true; | ||
| 899 | } | ||
| 900 | |||
| 901 | /// Obtain only the data needed to maintain a lock on the manifest file. | ||
| 902 | /// The `Manifest` remains safe to deinit. | ||
| 903 | /// Don't forget to call `writeManifest` before this! | ||
| 904 | pub fn toOwnedLock(self: *Manifest) Lock { | ||
| 905 | const lock: Lock = .{ | ||
| 906 | .manifest_file = self.manifest_file.?, | ||
| 907 | }; | ||
| 908 | |||
| 909 | self.manifest_file = null; | ||
| 910 | return lock; | ||
| 911 | } | ||
| 912 | |||
| 913 | /// Releases the manifest file and frees any memory the Manifest was using. | ||
| 914 | /// `Manifest.hit` must be called first. | ||
| 915 | /// Don't forget to call `writeManifest` before this! | ||
| 916 | pub fn deinit(self: *Manifest) void { | ||
| 917 | if (self.manifest_file) |file| { | ||
| 918 | if (builtin.os.tag == .windows) { | ||
| 919 | // See Lock.release for why this is required on Windows | ||
| 920 | file.unlock(); | ||
| 921 | } | ||
| 922 | |||
| 923 | file.close(); | ||
| 924 | } | ||
| 925 | for (self.files.items) |*file| { | ||
| 926 | file.deinit(self.cache.gpa); | ||
| 927 | } | ||
| 928 | self.files.deinit(self.cache.gpa); | ||
| 929 | } | ||
| 930 | }; | ||
| 931 | |||
| 932 | /// On operating systems that support symlinks, does a readlink. On other operating systems, | ||
| 933 | /// uses the file contents. Windows supports symlinks but only with elevated privileges, so | ||
| 934 | /// it is treated as not supporting symlinks. | ||
| 935 | pub fn readSmallFile(dir: fs.Dir, sub_path: []const u8, buffer: []u8) ![]u8 { | ||
| 936 | if (builtin.os.tag == .windows) { | ||
| 937 | return dir.readFile(sub_path, buffer); | ||
| 938 | } else { | ||
| 939 | return dir.readLink(sub_path, buffer); | ||
| 940 | } | ||
| 941 | } | ||
| 942 | |||
| 943 | /// On operating systems that support symlinks, does a symlink. On other operating systems, | ||
| 944 | /// uses the file contents. Windows supports symlinks but only with elevated privileges, so | ||
| 945 | /// it is treated as not supporting symlinks. | ||
| 946 | /// `data` must be a valid UTF-8 encoded file path and 255 bytes or fewer. | ||
| 947 | pub fn writeSmallFile(dir: fs.Dir, sub_path: []const u8, data: []const u8) !void { | ||
| 948 | assert(data.len <= 255); | ||
| 949 | if (builtin.os.tag == .windows) { | ||
| 950 | return dir.writeFile(sub_path, data); | ||
| 951 | } else { | ||
| 952 | return dir.symLink(data, sub_path, .{}); | ||
| 953 | } | ||
| 954 | } | ||
| 955 | |||
| 956 | fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void { | ||
| 957 | var buf: [1024]u8 = undefined; | ||
| 958 | |||
| 959 | var hasher = hasher_init; | ||
| 960 | while (true) { | ||
| 961 | const bytes_read = try file.read(&buf); | ||
| 962 | if (bytes_read == 0) break; | ||
| 963 | hasher.update(buf[0..bytes_read]); | ||
| 964 | } | ||
| 965 | |||
| 966 | hasher.final(bin_digest); | ||
| 967 | } | ||
| 968 | |||
| 969 | // Create/Write a file, close it, then grab its stat.mtime timestamp. | ||
| 970 | fn testGetCurrentFileTimestamp() !i128 { | ||
| 971 | var file = try fs.cwd().createFile("test-filetimestamp.tmp", .{ | ||
| 972 | .read = true, | ||
| 973 | .truncate = true, | ||
| 974 | }); | ||
| 975 | defer file.close(); | ||
| 976 | |||
| 977 | return (try file.stat()).mtime; | ||
| 978 | } | ||
| 979 | |||
| 980 | test "cache file and then recall it" { | ||
| 981 | if (builtin.os.tag == .wasi) { | ||
| 982 | // https://github.com/ziglang/zig/issues/5437 | ||
| 983 | return error.SkipZigTest; | ||
| 984 | } | ||
| 985 | |||
| 986 | const cwd = fs.cwd(); | ||
| 987 | |||
| 988 | const temp_file = "test.txt"; | ||
| 989 | const temp_manifest_dir = "temp_manifest_dir"; | ||
| 990 | |||
| 991 | try cwd.writeFile(temp_file, "Hello, world!\n"); | ||
| 992 | |||
| 993 | // Wait for file timestamps to tick | ||
| 994 | const initial_time = try testGetCurrentFileTimestamp(); | ||
| 995 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | ||
| 996 | std.time.sleep(1); | ||
| 997 | } | ||
| 998 | |||
| 999 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 1000 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 1001 | |||
| 1002 | { | ||
| 1003 | var cache = Cache{ | ||
| 1004 | .gpa = testing.allocator, | ||
| 1005 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 1006 | }; | ||
| 1007 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1008 | defer cache.manifest_dir.close(); | ||
| 1009 | |||
| 1010 | { | ||
| 1011 | var ch = cache.obtain(); | ||
| 1012 | defer ch.deinit(); | ||
| 1013 | |||
| 1014 | ch.hash.add(true); | ||
| 1015 | ch.hash.add(@as(u16, 1234)); | ||
| 1016 | ch.hash.addBytes("1234"); | ||
| 1017 | _ = try ch.addFile(temp_file, null); | ||
| 1018 | |||
| 1019 | // There should be nothing in the cache | ||
| 1020 | try testing.expectEqual(false, try ch.hit()); | ||
| 1021 | |||
| 1022 | digest1 = ch.final(); | ||
| 1023 | try ch.writeManifest(); | ||
| 1024 | } | ||
| 1025 | { | ||
| 1026 | var ch = cache.obtain(); | ||
| 1027 | defer ch.deinit(); | ||
| 1028 | |||
| 1029 | ch.hash.add(true); | ||
| 1030 | ch.hash.add(@as(u16, 1234)); | ||
| 1031 | ch.hash.addBytes("1234"); | ||
| 1032 | _ = try ch.addFile(temp_file, null); | ||
| 1033 | |||
| 1034 | // Cache hit! We just "built" the same file | ||
| 1035 | try testing.expect(try ch.hit()); | ||
| 1036 | digest2 = ch.final(); | ||
| 1037 | |||
| 1038 | try testing.expectEqual(false, ch.have_exclusive_lock); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | try testing.expectEqual(digest1, digest2); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1045 | try cwd.deleteFile(temp_file); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | test "check that changing a file makes cache fail" { | ||
| 1049 | if (builtin.os.tag == .wasi) { | ||
| 1050 | // https://github.com/ziglang/zig/issues/5437 | ||
| 1051 | return error.SkipZigTest; | ||
| 1052 | } | ||
| 1053 | const cwd = fs.cwd(); | ||
| 1054 | |||
| 1055 | const temp_file = "cache_hash_change_file_test.txt"; | ||
| 1056 | const temp_manifest_dir = "cache_hash_change_file_manifest_dir"; | ||
| 1057 | const original_temp_file_contents = "Hello, world!\n"; | ||
| 1058 | const updated_temp_file_contents = "Hello, world; but updated!\n"; | ||
| 1059 | |||
| 1060 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1061 | try cwd.deleteTree(temp_file); | ||
| 1062 | |||
| 1063 | try cwd.writeFile(temp_file, original_temp_file_contents); | ||
| 1064 | |||
| 1065 | // Wait for file timestamps to tick | ||
| 1066 | const initial_time = try testGetCurrentFileTimestamp(); | ||
| 1067 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | ||
| 1068 | std.time.sleep(1); | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 1072 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 1073 | |||
| 1074 | { | ||
| 1075 | var cache = Cache{ | ||
| 1076 | .gpa = testing.allocator, | ||
| 1077 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 1078 | }; | ||
| 1079 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1080 | defer cache.manifest_dir.close(); | ||
| 1081 | |||
| 1082 | { | ||
| 1083 | var ch = cache.obtain(); | ||
| 1084 | defer ch.deinit(); | ||
| 1085 | |||
| 1086 | ch.hash.addBytes("1234"); | ||
| 1087 | const temp_file_idx = try ch.addFile(temp_file, 100); | ||
| 1088 | |||
| 1089 | // There should be nothing in the cache | ||
| 1090 | try testing.expectEqual(false, try ch.hit()); | ||
| 1091 | |||
| 1092 | try testing.expect(mem.eql(u8, original_temp_file_contents, ch.files.items[temp_file_idx].contents.?)); | ||
| 1093 | |||
| 1094 | digest1 = ch.final(); | ||
| 1095 | |||
| 1096 | try ch.writeManifest(); | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | try cwd.writeFile(temp_file, updated_temp_file_contents); | ||
| 1100 | |||
| 1101 | { | ||
| 1102 | var ch = cache.obtain(); | ||
| 1103 | defer ch.deinit(); | ||
| 1104 | |||
| 1105 | ch.hash.addBytes("1234"); | ||
| 1106 | const temp_file_idx = try ch.addFile(temp_file, 100); | ||
| 1107 | |||
| 1108 | // A file that we depend on has been updated, so the cache should not contain an entry for it | ||
| 1109 | try testing.expectEqual(false, try ch.hit()); | ||
| 1110 | |||
| 1111 | // The cache system does not keep the contents of re-hashed input files. | ||
| 1112 | try testing.expect(ch.files.items[temp_file_idx].contents == null); | ||
| 1113 | |||
| 1114 | digest2 = ch.final(); | ||
| 1115 | |||
| 1116 | try ch.writeManifest(); | ||
| 1117 | } | ||
| 1118 | |||
| 1119 | try testing.expect(!mem.eql(u8, digest1[0..], digest2[0..])); | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1123 | try cwd.deleteTree(temp_file); | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | test "no file inputs" { | ||
| 1127 | if (builtin.os.tag == .wasi) { | ||
| 1128 | // https://github.com/ziglang/zig/issues/5437 | ||
| 1129 | return error.SkipZigTest; | ||
| 1130 | } | ||
| 1131 | const cwd = fs.cwd(); | ||
| 1132 | const temp_manifest_dir = "no_file_inputs_manifest_dir"; | ||
| 1133 | defer cwd.deleteTree(temp_manifest_dir) catch {}; | ||
| 1134 | |||
| 1135 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 1136 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 1137 | |||
| 1138 | var cache = Cache{ | ||
| 1139 | .gpa = testing.allocator, | ||
| 1140 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 1141 | }; | ||
| 1142 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1143 | defer cache.manifest_dir.close(); | ||
| 1144 | |||
| 1145 | { | ||
| 1146 | var man = cache.obtain(); | ||
| 1147 | defer man.deinit(); | ||
| 1148 | |||
| 1149 | man.hash.addBytes("1234"); | ||
| 1150 | |||
| 1151 | // There should be nothing in the cache | ||
| 1152 | try testing.expectEqual(false, try man.hit()); | ||
| 1153 | |||
| 1154 | digest1 = man.final(); | ||
| 1155 | |||
| 1156 | try man.writeManifest(); | ||
| 1157 | } | ||
| 1158 | { | ||
| 1159 | var man = cache.obtain(); | ||
| 1160 | defer man.deinit(); | ||
| 1161 | |||
| 1162 | man.hash.addBytes("1234"); | ||
| 1163 | |||
| 1164 | try testing.expect(try man.hit()); | ||
| 1165 | digest2 = man.final(); | ||
| 1166 | try testing.expectEqual(false, man.have_exclusive_lock); | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | try testing.expectEqual(digest1, digest2); | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | test "Manifest with files added after initial hash work" { | ||
| 1173 | if (builtin.os.tag == .wasi) { | ||
| 1174 | // https://github.com/ziglang/zig/issues/5437 | ||
| 1175 | return error.SkipZigTest; | ||
| 1176 | } | ||
| 1177 | const cwd = fs.cwd(); | ||
| 1178 | |||
| 1179 | const temp_file1 = "cache_hash_post_file_test1.txt"; | ||
| 1180 | const temp_file2 = "cache_hash_post_file_test2.txt"; | ||
| 1181 | const temp_manifest_dir = "cache_hash_post_file_manifest_dir"; | ||
| 1182 | |||
| 1183 | try cwd.writeFile(temp_file1, "Hello, world!\n"); | ||
| 1184 | try cwd.writeFile(temp_file2, "Hello world the second!\n"); | ||
| 1185 | |||
| 1186 | // Wait for file timestamps to tick | ||
| 1187 | const initial_time = try testGetCurrentFileTimestamp(); | ||
| 1188 | while ((try testGetCurrentFileTimestamp()) == initial_time) { | ||
| 1189 | std.time.sleep(1); | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | var digest1: [hex_digest_len]u8 = undefined; | ||
| 1193 | var digest2: [hex_digest_len]u8 = undefined; | ||
| 1194 | var digest3: [hex_digest_len]u8 = undefined; | ||
| 1195 | |||
| 1196 | { | ||
| 1197 | var cache = Cache{ | ||
| 1198 | .gpa = testing.allocator, | ||
| 1199 | .manifest_dir = try cwd.makeOpenPath(temp_manifest_dir, .{}), | ||
| 1200 | }; | ||
| 1201 | cache.addPrefix(.{ .path = null, .handle = fs.cwd() }); | ||
| 1202 | defer cache.manifest_dir.close(); | ||
| 1203 | |||
| 1204 | { | ||
| 1205 | var ch = cache.obtain(); | ||
| 1206 | defer ch.deinit(); | ||
| 1207 | |||
| 1208 | ch.hash.addBytes("1234"); | ||
| 1209 | _ = try ch.addFile(temp_file1, null); | ||
| 1210 | |||
| 1211 | // There should be nothing in the cache | ||
| 1212 | try testing.expectEqual(false, try ch.hit()); | ||
| 1213 | |||
| 1214 | _ = try ch.addFilePost(temp_file2); | ||
| 1215 | |||
| 1216 | digest1 = ch.final(); | ||
| 1217 | try ch.writeManifest(); | ||
| 1218 | } | ||
| 1219 | { | ||
| 1220 | var ch = cache.obtain(); | ||
| 1221 | defer ch.deinit(); | ||
| 1222 | |||
| 1223 | ch.hash.addBytes("1234"); | ||
| 1224 | _ = try ch.addFile(temp_file1, null); | ||
| 1225 | |||
| 1226 | try testing.expect(try ch.hit()); | ||
| 1227 | digest2 = ch.final(); | ||
| 1228 | |||
| 1229 | try testing.expectEqual(false, ch.have_exclusive_lock); | ||
| 1230 | } | ||
| 1231 | try testing.expect(mem.eql(u8, &digest1, &digest2)); | ||
| 1232 | |||
| 1233 | // Modify the file added after initial hash | ||
| 1234 | try cwd.writeFile(temp_file2, "Hello world the second, updated\n"); | ||
| 1235 | |||
| 1236 | // Wait for file timestamps to tick | ||
| 1237 | const initial_time2 = try testGetCurrentFileTimestamp(); | ||
| 1238 | while ((try testGetCurrentFileTimestamp()) == initial_time2) { | ||
| 1239 | std.time.sleep(1); | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | { | ||
| 1243 | var ch = cache.obtain(); | ||
| 1244 | defer ch.deinit(); | ||
| 1245 | |||
| 1246 | ch.hash.addBytes("1234"); | ||
| 1247 | _ = try ch.addFile(temp_file1, null); | ||
| 1248 | |||
| 1249 | // A file that we depend on has been updated, so the cache should not contain an entry for it | ||
| 1250 | try testing.expectEqual(false, try ch.hit()); | ||
| 1251 | |||
| 1252 | _ = try ch.addFilePost(temp_file2); | ||
| 1253 | |||
| 1254 | digest3 = ch.final(); | ||
| 1255 | |||
| 1256 | try ch.writeManifest(); | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | try testing.expect(!mem.eql(u8, &digest1, &digest3)); | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | try cwd.deleteTree(temp_manifest_dir); | ||
| 1263 | try cwd.deleteFile(temp_file1); | ||
| 1264 | try cwd.deleteFile(temp_file2); | ||
| 1265 | } | ||
src/Compilation.zig+87-62| ... | @@ -26,7 +26,7 @@ const wasi_libc = @import("wasi_libc.zig"); | ... | @@ -26,7 +26,7 @@ const wasi_libc = @import("wasi_libc.zig"); |
| 26 | const fatal = @import("main.zig").fatal; | 26 | const fatal = @import("main.zig").fatal; |
| 27 | const clangMain = @import("main.zig").clangMain; | 27 | const clangMain = @import("main.zig").clangMain; |
| 28 | const Module = @import("Module.zig"); | 28 | const Module = @import("Module.zig"); |
| 29 | const Cache = @import("Cache.zig"); | 29 | const Cache = std.Build.Cache; |
| 30 | const translate_c = @import("translate_c.zig"); | 30 | const translate_c = @import("translate_c.zig"); |
| 31 | const clang = @import("clang.zig"); | 31 | const clang = @import("clang.zig"); |
| 32 | const c_codegen = @import("codegen/c.zig"); | 32 | const c_codegen = @import("codegen/c.zig"); |
| ... | @@ -807,44 +807,7 @@ pub const AllErrors = struct { | ... | @@ -807,44 +807,7 @@ pub const AllErrors = struct { |
| 807 | } | 807 | } |
| 808 | }; | 808 | }; |
| 809 | 809 | ||
| 810 | pub const Directory = struct { | 810 | pub const Directory = Cache.Directory; |
| 811 | /// This field is redundant for operations that can act on the open directory handle | ||
| 812 | /// directly, but it is needed when passing the directory to a child process. | ||
| 813 | /// `null` means cwd. | ||
| 814 | path: ?[]const u8, | ||
| 815 | handle: std.fs.Dir, | ||
| 816 | |||
| 817 | pub fn join(self: Directory, allocator: Allocator, paths: []const []const u8) ![]u8 { | ||
| 818 | if (self.path) |p| { | ||
| 819 | // TODO clean way to do this with only 1 allocation | ||
| 820 | const part2 = try std.fs.path.join(allocator, paths); | ||
| 821 | defer allocator.free(part2); | ||
| 822 | return std.fs.path.join(allocator, &[_][]const u8{ p, part2 }); | ||
| 823 | } else { | ||
| 824 | return std.fs.path.join(allocator, paths); | ||
| 825 | } | ||
| 826 | } | ||
| 827 | |||
| 828 | pub fn joinZ(self: Directory, allocator: Allocator, paths: []const []const u8) ![:0]u8 { | ||
| 829 | if (self.path) |p| { | ||
| 830 | // TODO clean way to do this with only 1 allocation | ||
| 831 | const part2 = try std.fs.path.join(allocator, paths); | ||
| 832 | defer allocator.free(part2); | ||
| 833 | return std.fs.path.joinZ(allocator, &[_][]const u8{ p, part2 }); | ||
| 834 | } else { | ||
| 835 | return std.fs.path.joinZ(allocator, paths); | ||
| 836 | } | ||
| 837 | } | ||
| 838 | |||
| 839 | /// Whether or not the handle should be closed, or the path should be freed | ||
| 840 | /// is determined by usage, however this function is provided for convenience | ||
| 841 | /// if it happens to be what the caller needs. | ||
| 842 | pub fn closeAndFree(self: *Directory, gpa: Allocator) void { | ||
| 843 | self.handle.close(); | ||
| 844 | if (self.path) |p| gpa.free(p); | ||
| 845 | self.* = undefined; | ||
| 846 | } | ||
| 847 | }; | ||
| 848 | 811 | ||
| 849 | pub const EmitLoc = struct { | 812 | pub const EmitLoc = struct { |
| 850 | /// If this is `null` it means the file will be output to the cache directory. | 813 | /// If this is `null` it means the file will be output to the cache directory. |
| ... | @@ -854,6 +817,35 @@ pub const EmitLoc = struct { | ... | @@ -854,6 +817,35 @@ pub const EmitLoc = struct { |
| 854 | basename: []const u8, | 817 | basename: []const u8, |
| 855 | }; | 818 | }; |
| 856 | 819 | ||
| 820 | pub const cache_helpers = struct { | ||
| 821 | pub fn addEmitLoc(hh: *Cache.HashHelper, emit_loc: EmitLoc) void { | ||
| 822 | hh.addBytes(emit_loc.basename); | ||
| 823 | } | ||
| 824 | |||
| 825 | pub fn addOptionalEmitLoc(hh: *Cache.HashHelper, optional_emit_loc: ?EmitLoc) void { | ||
| 826 | hh.add(optional_emit_loc != null); | ||
| 827 | addEmitLoc(hh, optional_emit_loc orelse return); | ||
| 828 | } | ||
| 829 | |||
| 830 | pub fn hashCSource(self: *Cache.Manifest, c_source: Compilation.CSourceFile) !void { | ||
| 831 | _ = try self.addFile(c_source.src_path, null); | ||
| 832 | // Hash the extra flags, with special care to call addFile for file parameters. | ||
| 833 | // TODO this logic can likely be improved by utilizing clang_options_data.zig. | ||
| 834 | const file_args = [_][]const u8{"-include"}; | ||
| 835 | var arg_i: usize = 0; | ||
| 836 | while (arg_i < c_source.extra_flags.len) : (arg_i += 1) { | ||
| 837 | const arg = c_source.extra_flags[arg_i]; | ||
| 838 | self.hash.addBytes(arg); | ||
| 839 | for (file_args) |file_arg| { | ||
| 840 | if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) { | ||
| 841 | arg_i += 1; | ||
| 842 | _ = try self.addFile(c_source.extra_flags[arg_i], null); | ||
| 843 | } | ||
| 844 | } | ||
| 845 | } | ||
| 846 | } | ||
| 847 | }; | ||
| 848 | |||
| 857 | pub const ClangPreprocessorMode = enum { | 849 | pub const ClangPreprocessorMode = enum { |
| 858 | no, | 850 | no, |
| 859 | /// This means we are doing `zig cc -E -o <path>`. | 851 | /// This means we are doing `zig cc -E -o <path>`. |
| ... | @@ -997,6 +989,7 @@ pub const InitOptions = struct { | ... | @@ -997,6 +989,7 @@ pub const InitOptions = struct { |
| 997 | linker_optimization: ?u8 = null, | 989 | linker_optimization: ?u8 = null, |
| 998 | linker_compress_debug_sections: ?link.CompressDebugSections = null, | 990 | linker_compress_debug_sections: ?link.CompressDebugSections = null, |
| 999 | linker_module_definition_file: ?[]const u8 = null, | 991 | linker_module_definition_file: ?[]const u8 = null, |
| 992 | linker_sort_section: ?link.SortSection = null, | ||
| 1000 | major_subsystem_version: ?u32 = null, | 993 | major_subsystem_version: ?u32 = null, |
| 1001 | minor_subsystem_version: ?u32 = null, | 994 | minor_subsystem_version: ?u32 = null, |
| 1002 | clang_passthrough_mode: bool = false, | 995 | clang_passthrough_mode: bool = false, |
| ... | @@ -1029,6 +1022,7 @@ pub const InitOptions = struct { | ... | @@ -1029,6 +1022,7 @@ pub const InitOptions = struct { |
| 1029 | /// This is for stage1 and should be deleted upon completion of self-hosting. | 1022 | /// This is for stage1 and should be deleted upon completion of self-hosting. |
| 1030 | color: Color = .auto, | 1023 | color: Color = .auto, |
| 1031 | reference_trace: ?u32 = null, | 1024 | reference_trace: ?u32 = null, |
| 1025 | error_tracing: ?bool = null, | ||
| 1032 | test_filter: ?[]const u8 = null, | 1026 | test_filter: ?[]const u8 = null, |
| 1033 | test_name_prefix: ?[]const u8 = null, | 1027 | test_name_prefix: ?[]const u8 = null, |
| 1034 | test_runner_path: ?[]const u8 = null, | 1028 | test_runner_path: ?[]const u8 = null, |
| ... | @@ -1522,8 +1516,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1522,8 +1516,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1522 | cache.hash.add(link_libunwind); | 1516 | cache.hash.add(link_libunwind); |
| 1523 | cache.hash.add(options.output_mode); | 1517 | cache.hash.add(options.output_mode); |
| 1524 | cache.hash.add(options.machine_code_model); | 1518 | cache.hash.add(options.machine_code_model); |
| 1525 | cache.hash.addOptionalEmitLoc(options.emit_bin); | 1519 | cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin); |
| 1526 | cache.hash.addOptionalEmitLoc(options.emit_implib); | 1520 | cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib); |
| 1527 | cache.hash.addBytes(options.root_name); | 1521 | cache.hash.addBytes(options.root_name); |
| 1528 | if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model); | 1522 | if (options.target.os.tag == .wasi) cache.hash.add(wasi_exec_model); |
| 1529 | // TODO audit this and make sure everything is in it | 1523 | // TODO audit this and make sure everything is in it |
| ... | @@ -1621,26 +1615,46 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1621,26 +1615,46 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1621 | const root_pkg = if (options.is_test) root_pkg: { | 1615 | const root_pkg = if (options.is_test) root_pkg: { |
| 1622 | // TODO: we currently have two packages named 'root' here, which is weird. This | 1616 | // TODO: we currently have two packages named 'root' here, which is weird. This |
| 1623 | // should be changed as part of the resolution of #12201 | 1617 | // should be changed as part of the resolution of #12201 |
| 1624 | const test_pkg = if (options.test_runner_path) |test_runner| | 1618 | const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: { |
| 1625 | try Package.create(gpa, "root", null, test_runner) | 1619 | const test_dir = std.fs.path.dirname(test_runner); |
| 1626 | else | 1620 | const basename = std.fs.path.basename(test_runner); |
| 1627 | try Package.createWithDir( | 1621 | const pkg = try Package.create(gpa, "root", test_dir, basename); |
| 1628 | gpa, | 1622 | |
| 1629 | "root", | 1623 | // copy package table from main_pkg to root_pkg |
| 1630 | options.zig_lib_directory, | 1624 | pkg.table = try main_pkg.table.clone(gpa); |
| 1631 | null, | 1625 | break :test_pkg pkg; |
| 1632 | "test_runner.zig", | 1626 | } else try Package.createWithDir( |
| 1633 | ); | 1627 | gpa, |
| 1628 | "root", | ||
| 1629 | options.zig_lib_directory, | ||
| 1630 | null, | ||
| 1631 | "test_runner.zig", | ||
| 1632 | ); | ||
| 1634 | errdefer test_pkg.destroy(gpa); | 1633 | errdefer test_pkg.destroy(gpa); |
| 1635 | 1634 | ||
| 1636 | break :root_pkg test_pkg; | 1635 | break :root_pkg test_pkg; |
| 1637 | } else main_pkg; | 1636 | } else main_pkg; |
| 1638 | errdefer if (options.is_test) root_pkg.destroy(gpa); | 1637 | errdefer if (options.is_test) root_pkg.destroy(gpa); |
| 1639 | 1638 | ||
| 1639 | const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: { | ||
| 1640 | break :compiler_rt_pkg try Package.createWithDir( | ||
| 1641 | gpa, | ||
| 1642 | "compiler_rt", | ||
| 1643 | options.zig_lib_directory, | ||
| 1644 | null, | ||
| 1645 | "compiler_rt.zig", | ||
| 1646 | ); | ||
| 1647 | } else null; | ||
| 1648 | errdefer if (compiler_rt_pkg) |p| p.destroy(gpa); | ||
| 1649 | |||
| 1640 | try main_pkg.addAndAdopt(gpa, builtin_pkg); | 1650 | try main_pkg.addAndAdopt(gpa, builtin_pkg); |
| 1641 | try main_pkg.add(gpa, root_pkg); | 1651 | try main_pkg.add(gpa, root_pkg); |
| 1642 | try main_pkg.addAndAdopt(gpa, std_pkg); | 1652 | try main_pkg.addAndAdopt(gpa, std_pkg); |
| 1643 | 1653 | ||
| 1654 | if (compiler_rt_pkg) |p| { | ||
| 1655 | try main_pkg.addAndAdopt(gpa, p); | ||
| 1656 | } | ||
| 1657 | |||
| 1644 | const main_pkg_is_std = m: { | 1658 | const main_pkg_is_std = m: { |
| 1645 | const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ | 1659 | const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ |
| 1646 | std_pkg.root_src_directory.path orelse ".", | 1660 | std_pkg.root_src_directory.path orelse ".", |
| ... | @@ -1710,8 +1724,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1710,8 +1724,9 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1710 | 1724 | ||
| 1711 | const error_return_tracing = !strip and switch (options.optimize_mode) { | 1725 | const error_return_tracing = !strip and switch (options.optimize_mode) { |
| 1712 | .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and | 1726 | .Debug, .ReleaseSafe => (!options.target.isWasm() or options.target.os.tag == .emscripten) and |
| 1713 | !options.target.cpu.arch.isBpf(), | 1727 | !options.target.cpu.arch.isBpf() and (options.error_tracing orelse true), |
| 1714 | .ReleaseFast, .ReleaseSmall => false, | 1728 | .ReleaseFast => options.error_tracing orelse false, |
| 1729 | .ReleaseSmall => false, | ||
| 1715 | }; | 1730 | }; |
| 1716 | 1731 | ||
| 1717 | // For resource management purposes. | 1732 | // For resource management purposes. |
| ... | @@ -1839,6 +1854,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { | ... | @@ -1839,6 +1854,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { |
| 1839 | .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, | 1854 | .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, |
| 1840 | .compress_debug_sections = options.linker_compress_debug_sections orelse .none, | 1855 | .compress_debug_sections = options.linker_compress_debug_sections orelse .none, |
| 1841 | .module_definition_file = options.linker_module_definition_file, | 1856 | .module_definition_file = options.linker_module_definition_file, |
| 1857 | .sort_section = options.linker_sort_section, | ||
| 1842 | .import_memory = options.linker_import_memory orelse false, | 1858 | .import_memory = options.linker_import_memory orelse false, |
| 1843 | .import_symbols = options.linker_import_symbols, | 1859 | .import_symbols = options.linker_import_symbols, |
| 1844 | .import_table = options.linker_import_table, | 1860 | .import_table = options.linker_import_table, |
| ... | @@ -2356,6 +2372,10 @@ pub fn update(comp: *Compilation) !void { | ... | @@ -2356,6 +2372,10 @@ pub fn update(comp: *Compilation) !void { |
| 2356 | _ = try module.importPkg(module.main_pkg); | 2372 | _ = try module.importPkg(module.main_pkg); |
| 2357 | } | 2373 | } |
| 2358 | 2374 | ||
| 2375 | if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| { | ||
| 2376 | _ = try module.importPkg(compiler_rt_pkg); | ||
| 2377 | } | ||
| 2378 | |||
| 2359 | // Put a work item in for every known source file to detect if | 2379 | // Put a work item in for every known source file to detect if |
| 2360 | // it changed, and, if so, re-compute ZIR and then queue the job | 2380 | // it changed, and, if so, re-compute ZIR and then queue the job |
| 2361 | // to update it. | 2381 | // to update it. |
| ... | @@ -2380,6 +2400,10 @@ pub fn update(comp: *Compilation) !void { | ... | @@ -2380,6 +2400,10 @@ pub fn update(comp: *Compilation) !void { |
| 2380 | if (comp.bin_file.options.is_test) { | 2400 | if (comp.bin_file.options.is_test) { |
| 2381 | try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); | 2401 | try comp.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); |
| 2382 | } | 2402 | } |
| 2403 | |||
| 2404 | if (module.main_pkg.table.get("compiler_rt")) |compiler_rt_pkg| { | ||
| 2405 | try comp.work_queue.writeItem(.{ .analyze_pkg = compiler_rt_pkg }); | ||
| 2406 | } | ||
| 2383 | } | 2407 | } |
| 2384 | 2408 | ||
| 2385 | // If the terminal is dumb, we dont want to show the user all the output. | 2409 | // If the terminal is dumb, we dont want to show the user all the output. |
| ... | @@ -2631,11 +2655,11 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes | ... | @@ -2631,11 +2655,11 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2631 | man.hash.addListOfBytes(key.src.extra_flags); | 2655 | man.hash.addListOfBytes(key.src.extra_flags); |
| 2632 | } | 2656 | } |
| 2633 | 2657 | ||
| 2634 | man.hash.addOptionalEmitLoc(comp.emit_asm); | 2658 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); |
| 2635 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); | 2659 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); |
| 2636 | man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); | 2660 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc); |
| 2637 | man.hash.addOptionalEmitLoc(comp.emit_analysis); | 2661 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_analysis); |
| 2638 | man.hash.addOptionalEmitLoc(comp.emit_docs); | 2662 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_docs); |
| 2639 | 2663 | ||
| 2640 | man.hash.addListOfBytes(comp.clang_argv); | 2664 | man.hash.addListOfBytes(comp.clang_argv); |
| 2641 | 2665 | ||
| ... | @@ -2662,6 +2686,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes | ... | @@ -2662,6 +2686,7 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes |
| 2662 | man.hash.add(comp.bin_file.options.hash_style); | 2686 | man.hash.add(comp.bin_file.options.hash_style); |
| 2663 | man.hash.add(comp.bin_file.options.compress_debug_sections); | 2687 | man.hash.add(comp.bin_file.options.compress_debug_sections); |
| 2664 | man.hash.add(comp.bin_file.options.include_compiler_rt); | 2688 | man.hash.add(comp.bin_file.options.include_compiler_rt); |
| 2689 | man.hash.addOptional(comp.bin_file.options.sort_section); | ||
| 2665 | if (comp.bin_file.options.link_libc) { | 2690 | if (comp.bin_file.options.link_libc) { |
| 2666 | man.hash.add(comp.bin_file.options.libc_installation != null); | 2691 | man.hash.add(comp.bin_file.options.libc_installation != null); |
| 2667 | if (comp.bin_file.options.libc_installation) |libc_installation| { | 2692 | if (comp.bin_file.options.libc_installation) |libc_installation| { |
| ... | @@ -3954,11 +3979,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -3954,11 +3979,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 3954 | defer man.deinit(); | 3979 | defer man.deinit(); |
| 3955 | 3980 | ||
| 3956 | man.hash.add(comp.clang_preprocessor_mode); | 3981 | man.hash.add(comp.clang_preprocessor_mode); |
| 3957 | man.hash.addOptionalEmitLoc(comp.emit_asm); | 3982 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); |
| 3958 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); | 3983 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_ir); |
| 3959 | man.hash.addOptionalEmitLoc(comp.emit_llvm_bc); | 3984 | cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_llvm_bc); |
| 3960 | 3985 | ||
| 3961 | try man.hashCSource(c_object.src); | 3986 | try cache_helpers.hashCSource(&man, c_object.src); |
| 3962 | 3987 | ||
| 3963 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); | 3988 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); |
| 3964 | defer arena_allocator.deinit(); | 3989 | defer arena_allocator.deinit(); |
src/DepTokenizer.zig deleted-1069| ... | @@ -1,1069 +0,0 @@ | ||
| 1 | const Tokenizer = @This(); | ||
| 2 | |||
| 3 | index: usize = 0, | ||
| 4 | bytes: []const u8, | ||
| 5 | state: State = .lhs, | ||
| 6 | |||
| 7 | const std = @import("std"); | ||
| 8 | const testing = std.testing; | ||
| 9 | const assert = std.debug.assert; | ||
| 10 | |||
| 11 | pub fn next(self: *Tokenizer) ?Token { | ||
| 12 | var start = self.index; | ||
| 13 | var must_resolve = false; | ||
| 14 | while (self.index < self.bytes.len) { | ||
| 15 | const char = self.bytes[self.index]; | ||
| 16 | switch (self.state) { | ||
| 17 | .lhs => switch (char) { | ||
| 18 | '\t', '\n', '\r', ' ' => { | ||
| 19 | // silently ignore whitespace | ||
| 20 | self.index += 1; | ||
| 21 | }, | ||
| 22 | else => { | ||
| 23 | start = self.index; | ||
| 24 | self.state = .target; | ||
| 25 | }, | ||
| 26 | }, | ||
| 27 | .target => switch (char) { | ||
| 28 | '\t', '\n', '\r', ' ' => { | ||
| 29 | return errorIllegalChar(.invalid_target, self.index, char); | ||
| 30 | }, | ||
| 31 | '$' => { | ||
| 32 | self.state = .target_dollar_sign; | ||
| 33 | self.index += 1; | ||
| 34 | }, | ||
| 35 | '\\' => { | ||
| 36 | self.state = .target_reverse_solidus; | ||
| 37 | self.index += 1; | ||
| 38 | }, | ||
| 39 | ':' => { | ||
| 40 | self.state = .target_colon; | ||
| 41 | self.index += 1; | ||
| 42 | }, | ||
| 43 | else => { | ||
| 44 | self.index += 1; | ||
| 45 | }, | ||
| 46 | }, | ||
| 47 | .target_reverse_solidus => switch (char) { | ||
| 48 | '\t', '\n', '\r' => { | ||
| 49 | return errorIllegalChar(.bad_target_escape, self.index, char); | ||
| 50 | }, | ||
| 51 | ' ', '#', '\\' => { | ||
| 52 | must_resolve = true; | ||
| 53 | self.state = .target; | ||
| 54 | self.index += 1; | ||
| 55 | }, | ||
| 56 | '$' => { | ||
| 57 | self.state = .target_dollar_sign; | ||
| 58 | self.index += 1; | ||
| 59 | }, | ||
| 60 | else => { | ||
| 61 | self.state = .target; | ||
| 62 | self.index += 1; | ||
| 63 | }, | ||
| 64 | }, | ||
| 65 | .target_dollar_sign => switch (char) { | ||
| 66 | '$' => { | ||
| 67 | must_resolve = true; | ||
| 68 | self.state = .target; | ||
| 69 | self.index += 1; | ||
| 70 | }, | ||
| 71 | else => { | ||
| 72 | return errorIllegalChar(.expected_dollar_sign, self.index, char); | ||
| 73 | }, | ||
| 74 | }, | ||
| 75 | .target_colon => switch (char) { | ||
| 76 | '\n', '\r' => { | ||
| 77 | const bytes = self.bytes[start .. self.index - 1]; | ||
| 78 | if (bytes.len != 0) { | ||
| 79 | self.state = .lhs; | ||
| 80 | return finishTarget(must_resolve, bytes); | ||
| 81 | } | ||
| 82 | // silently ignore null target | ||
| 83 | self.state = .lhs; | ||
| 84 | }, | ||
| 85 | '/', '\\' => { | ||
| 86 | self.state = .target_colon_reverse_solidus; | ||
| 87 | self.index += 1; | ||
| 88 | }, | ||
| 89 | else => { | ||
| 90 | const bytes = self.bytes[start .. self.index - 1]; | ||
| 91 | if (bytes.len != 0) { | ||
| 92 | self.state = .rhs; | ||
| 93 | return finishTarget(must_resolve, bytes); | ||
| 94 | } | ||
| 95 | // silently ignore null target | ||
| 96 | self.state = .lhs; | ||
| 97 | }, | ||
| 98 | }, | ||
| 99 | .target_colon_reverse_solidus => switch (char) { | ||
| 100 | '\n', '\r' => { | ||
| 101 | const bytes = self.bytes[start .. self.index - 2]; | ||
| 102 | if (bytes.len != 0) { | ||
| 103 | self.state = .lhs; | ||
| 104 | return finishTarget(must_resolve, bytes); | ||
| 105 | } | ||
| 106 | // silently ignore null target | ||
| 107 | self.state = .lhs; | ||
| 108 | }, | ||
| 109 | else => { | ||
| 110 | self.state = .target; | ||
| 111 | }, | ||
| 112 | }, | ||
| 113 | .rhs => switch (char) { | ||
| 114 | '\t', ' ' => { | ||
| 115 | // silently ignore horizontal whitespace | ||
| 116 | self.index += 1; | ||
| 117 | }, | ||
| 118 | '\n', '\r' => { | ||
| 119 | self.state = .lhs; | ||
| 120 | }, | ||
| 121 | '\\' => { | ||
| 122 | self.state = .rhs_continuation; | ||
| 123 | self.index += 1; | ||
| 124 | }, | ||
| 125 | '"' => { | ||
| 126 | self.state = .prereq_quote; | ||
| 127 | self.index += 1; | ||
| 128 | start = self.index; | ||
| 129 | }, | ||
| 130 | else => { | ||
| 131 | start = self.index; | ||
| 132 | self.state = .prereq; | ||
| 133 | }, | ||
| 134 | }, | ||
| 135 | .rhs_continuation => switch (char) { | ||
| 136 | '\n' => { | ||
| 137 | self.state = .rhs; | ||
| 138 | self.index += 1; | ||
| 139 | }, | ||
| 140 | '\r' => { | ||
| 141 | self.state = .rhs_continuation_linefeed; | ||
| 142 | self.index += 1; | ||
| 143 | }, | ||
| 144 | else => { | ||
| 145 | return errorIllegalChar(.continuation_eol, self.index, char); | ||
| 146 | }, | ||
| 147 | }, | ||
| 148 | .rhs_continuation_linefeed => switch (char) { | ||
| 149 | '\n' => { | ||
| 150 | self.state = .rhs; | ||
| 151 | self.index += 1; | ||
| 152 | }, | ||
| 153 | else => { | ||
| 154 | return errorIllegalChar(.continuation_eol, self.index, char); | ||
| 155 | }, | ||
| 156 | }, | ||
| 157 | .prereq_quote => switch (char) { | ||
| 158 | '"' => { | ||
| 159 | self.index += 1; | ||
| 160 | self.state = .rhs; | ||
| 161 | return Token{ .prereq = self.bytes[start .. self.index - 1] }; | ||
| 162 | }, | ||
| 163 | else => { | ||
| 164 | self.index += 1; | ||
| 165 | }, | ||
| 166 | }, | ||
| 167 | .prereq => switch (char) { | ||
| 168 | '\t', ' ' => { | ||
| 169 | self.state = .rhs; | ||
| 170 | return Token{ .prereq = self.bytes[start..self.index] }; | ||
| 171 | }, | ||
| 172 | '\n', '\r' => { | ||
| 173 | self.state = .lhs; | ||
| 174 | return Token{ .prereq = self.bytes[start..self.index] }; | ||
| 175 | }, | ||
| 176 | '\\' => { | ||
| 177 | self.state = .prereq_continuation; | ||
| 178 | self.index += 1; | ||
| 179 | }, | ||
| 180 | else => { | ||
| 181 | self.index += 1; | ||
| 182 | }, | ||
| 183 | }, | ||
| 184 | .prereq_continuation => switch (char) { | ||
| 185 | '\n' => { | ||
| 186 | self.index += 1; | ||
| 187 | self.state = .rhs; | ||
| 188 | return Token{ .prereq = self.bytes[start .. self.index - 2] }; | ||
| 189 | }, | ||
| 190 | '\r' => { | ||
| 191 | self.state = .prereq_continuation_linefeed; | ||
| 192 | self.index += 1; | ||
| 193 | }, | ||
| 194 | else => { | ||
| 195 | // not continuation | ||
| 196 | self.state = .prereq; | ||
| 197 | self.index += 1; | ||
| 198 | }, | ||
| 199 | }, | ||
| 200 | .prereq_continuation_linefeed => switch (char) { | ||
| 201 | '\n' => { | ||
| 202 | self.index += 1; | ||
| 203 | self.state = .rhs; | ||
| 204 | return Token{ .prereq = self.bytes[start .. self.index - 1] }; | ||
| 205 | }, | ||
| 206 | else => { | ||
| 207 | return errorIllegalChar(.continuation_eol, self.index, char); | ||
| 208 | }, | ||
| 209 | }, | ||
| 210 | } | ||
| 211 | } else { | ||
| 212 | switch (self.state) { | ||
| 213 | .lhs, | ||
| 214 | .rhs, | ||
| 215 | .rhs_continuation, | ||
| 216 | .rhs_continuation_linefeed, | ||
| 217 | => return null, | ||
| 218 | .target => { | ||
| 219 | return errorPosition(.incomplete_target, start, self.bytes[start..]); | ||
| 220 | }, | ||
| 221 | .target_reverse_solidus, | ||
| 222 | .target_dollar_sign, | ||
| 223 | => { | ||
| 224 | const idx = self.index - 1; | ||
| 225 | return errorIllegalChar(.incomplete_escape, idx, self.bytes[idx]); | ||
| 226 | }, | ||
| 227 | .target_colon => { | ||
| 228 | const bytes = self.bytes[start .. self.index - 1]; | ||
| 229 | if (bytes.len != 0) { | ||
| 230 | self.index += 1; | ||
| 231 | self.state = .rhs; | ||
| 232 | return finishTarget(must_resolve, bytes); | ||
| 233 | } | ||
| 234 | // silently ignore null target | ||
| 235 | self.state = .lhs; | ||
| 236 | return null; | ||
| 237 | }, | ||
| 238 | .target_colon_reverse_solidus => { | ||
| 239 | const bytes = self.bytes[start .. self.index - 2]; | ||
| 240 | if (bytes.len != 0) { | ||
| 241 | self.index += 1; | ||
| 242 | self.state = .rhs; | ||
| 243 | return finishTarget(must_resolve, bytes); | ||
| 244 | } | ||
| 245 | // silently ignore null target | ||
| 246 | self.state = .lhs; | ||
| 247 | return null; | ||
| 248 | }, | ||
| 249 | .prereq_quote => { | ||
| 250 | return errorPosition(.incomplete_quoted_prerequisite, start, self.bytes[start..]); | ||
| 251 | }, | ||
| 252 | .prereq => { | ||
| 253 | self.state = .lhs; | ||
| 254 | return Token{ .prereq = self.bytes[start..] }; | ||
| 255 | }, | ||
| 256 | .prereq_continuation => { | ||
| 257 | self.state = .lhs; | ||
| 258 | return Token{ .prereq = self.bytes[start .. self.index - 1] }; | ||
| 259 | }, | ||
| 260 | .prereq_continuation_linefeed => { | ||
| 261 | self.state = .lhs; | ||
| 262 | return Token{ .prereq = self.bytes[start .. self.index - 2] }; | ||
| 263 | }, | ||
| 264 | } | ||
| 265 | } | ||
| 266 | unreachable; | ||
| 267 | } | ||
| 268 | |||
| 269 | fn errorPosition(comptime id: std.meta.Tag(Token), index: usize, bytes: []const u8) Token { | ||
| 270 | return @unionInit(Token, @tagName(id), .{ .index = index, .bytes = bytes }); | ||
| 271 | } | ||
| 272 | |||
| 273 | fn errorIllegalChar(comptime id: std.meta.Tag(Token), index: usize, char: u8) Token { | ||
| 274 | return @unionInit(Token, @tagName(id), .{ .index = index, .char = char }); | ||
| 275 | } | ||
| 276 | |||
| 277 | fn finishTarget(must_resolve: bool, bytes: []const u8) Token { | ||
| 278 | return if (must_resolve) .{ .target_must_resolve = bytes } else .{ .target = bytes }; | ||
| 279 | } | ||
| 280 | |||
| 281 | const State = enum { | ||
| 282 | lhs, | ||
| 283 | target, | ||
| 284 | target_reverse_solidus, | ||
| 285 | target_dollar_sign, | ||
| 286 | target_colon, | ||
| 287 | target_colon_reverse_solidus, | ||
| 288 | rhs, | ||
| 289 | rhs_continuation, | ||
| 290 | rhs_continuation_linefeed, | ||
| 291 | prereq_quote, | ||
| 292 | prereq, | ||
| 293 | prereq_continuation, | ||
| 294 | prereq_continuation_linefeed, | ||
| 295 | }; | ||
| 296 | |||
| 297 | pub const Token = union(enum) { | ||
| 298 | target: []const u8, | ||
| 299 | target_must_resolve: []const u8, | ||
| 300 | prereq: []const u8, | ||
| 301 | |||
| 302 | incomplete_quoted_prerequisite: IndexAndBytes, | ||
| 303 | incomplete_target: IndexAndBytes, | ||
| 304 | |||
| 305 | invalid_target: IndexAndChar, | ||
| 306 | bad_target_escape: IndexAndChar, | ||
| 307 | expected_dollar_sign: IndexAndChar, | ||
| 308 | continuation_eol: IndexAndChar, | ||
| 309 | incomplete_escape: IndexAndChar, | ||
| 310 | |||
| 311 | pub const IndexAndChar = struct { | ||
| 312 | index: usize, | ||
| 313 | char: u8, | ||
| 314 | }; | ||
| 315 | |||
| 316 | pub const IndexAndBytes = struct { | ||
| 317 | index: usize, | ||
| 318 | bytes: []const u8, | ||
| 319 | }; | ||
| 320 | |||
| 321 | /// Resolve escapes in target. Only valid with .target_must_resolve. | ||
| 322 | pub fn resolve(self: Token, writer: anytype) @TypeOf(writer).Error!void { | ||
| 323 | const bytes = self.target_must_resolve; // resolve called on incorrect token | ||
| 324 | |||
| 325 | var state: enum { start, escape, dollar } = .start; | ||
| 326 | for (bytes) |c| { | ||
| 327 | switch (state) { | ||
| 328 | .start => { | ||
| 329 | switch (c) { | ||
| 330 | '\\' => state = .escape, | ||
| 331 | '$' => state = .dollar, | ||
| 332 | else => try writer.writeByte(c), | ||
| 333 | } | ||
| 334 | }, | ||
| 335 | .escape => { | ||
| 336 | switch (c) { | ||
| 337 | ' ', '#', '\\' => {}, | ||
| 338 | '$' => { | ||
| 339 | try writer.writeByte('\\'); | ||
| 340 | state = .dollar; | ||
| 341 | continue; | ||
| 342 | }, | ||
| 343 | else => try writer.writeByte('\\'), | ||
| 344 | } | ||
| 345 | try writer.writeByte(c); | ||
| 346 | state = .start; | ||
| 347 | }, | ||
| 348 | .dollar => { | ||
| 349 | try writer.writeByte('$'); | ||
| 350 | switch (c) { | ||
| 351 | '$' => {}, | ||
| 352 | else => try writer.writeByte(c), | ||
| 353 | } | ||
| 354 | state = .start; | ||
| 355 | }, | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | pub fn printError(self: Token, writer: anytype) @TypeOf(writer).Error!void { | ||
| 361 | switch (self) { | ||
| 362 | .target, .target_must_resolve, .prereq => unreachable, // not an error | ||
| 363 | .incomplete_quoted_prerequisite, | ||
| 364 | .incomplete_target, | ||
| 365 | => |index_and_bytes| { | ||
| 366 | try writer.print("{s} '", .{self.errStr()}); | ||
| 367 | if (self == .incomplete_target) { | ||
| 368 | const tmp = Token{ .target_must_resolve = index_and_bytes.bytes }; | ||
| 369 | try tmp.resolve(writer); | ||
| 370 | } else { | ||
| 371 | try printCharValues(writer, index_and_bytes.bytes); | ||
| 372 | } | ||
| 373 | try writer.print("' at position {d}", .{index_and_bytes.index}); | ||
| 374 | }, | ||
| 375 | .invalid_target, | ||
| 376 | .bad_target_escape, | ||
| 377 | .expected_dollar_sign, | ||
| 378 | .continuation_eol, | ||
| 379 | .incomplete_escape, | ||
| 380 | => |index_and_char| { | ||
| 381 | try writer.writeAll("illegal char "); | ||
| 382 | try printUnderstandableChar(writer, index_and_char.char); | ||
| 383 | try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() }); | ||
| 384 | }, | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | fn errStr(self: Token) []const u8 { | ||
| 389 | return switch (self) { | ||
| 390 | .target, .target_must_resolve, .prereq => unreachable, // not an error | ||
| 391 | .incomplete_quoted_prerequisite => "incomplete quoted prerequisite", | ||
| 392 | .incomplete_target => "incomplete target", | ||
| 393 | .invalid_target => "invalid target", | ||
| 394 | .bad_target_escape => "bad target escape", | ||
| 395 | .expected_dollar_sign => "expecting '$'", | ||
| 396 | .continuation_eol => "continuation expecting end-of-line", | ||
| 397 | .incomplete_escape => "incomplete escape", | ||
| 398 | }; | ||
| 399 | } | ||
| 400 | }; | ||
| 401 | |||
| 402 | test "empty file" { | ||
| 403 | try depTokenizer("", ""); | ||
| 404 | } | ||
| 405 | |||
| 406 | test "empty whitespace" { | ||
| 407 | try depTokenizer("\n", ""); | ||
| 408 | try depTokenizer("\r", ""); | ||
| 409 | try depTokenizer("\r\n", ""); | ||
| 410 | try depTokenizer(" ", ""); | ||
| 411 | } | ||
| 412 | |||
| 413 | test "empty colon" { | ||
| 414 | try depTokenizer(":", ""); | ||
| 415 | try depTokenizer("\n:", ""); | ||
| 416 | try depTokenizer("\r:", ""); | ||
| 417 | try depTokenizer("\r\n:", ""); | ||
| 418 | try depTokenizer(" :", ""); | ||
| 419 | } | ||
| 420 | |||
| 421 | test "empty target" { | ||
| 422 | try depTokenizer("foo.o:", "target = {foo.o}"); | ||
| 423 | try depTokenizer( | ||
| 424 | \\foo.o: | ||
| 425 | \\bar.o: | ||
| 426 | \\abcd.o: | ||
| 427 | , | ||
| 428 | \\target = {foo.o} | ||
| 429 | \\target = {bar.o} | ||
| 430 | \\target = {abcd.o} | ||
| 431 | ); | ||
| 432 | } | ||
| 433 | |||
| 434 | test "whitespace empty target" { | ||
| 435 | try depTokenizer("\nfoo.o:", "target = {foo.o}"); | ||
| 436 | try depTokenizer("\rfoo.o:", "target = {foo.o}"); | ||
| 437 | try depTokenizer("\r\nfoo.o:", "target = {foo.o}"); | ||
| 438 | try depTokenizer(" foo.o:", "target = {foo.o}"); | ||
| 439 | } | ||
| 440 | |||
| 441 | test "escape empty target" { | ||
| 442 | try depTokenizer("\\ foo.o:", "target = { foo.o}"); | ||
| 443 | try depTokenizer("\\#foo.o:", "target = {#foo.o}"); | ||
| 444 | try depTokenizer("\\\\foo.o:", "target = {\\foo.o}"); | ||
| 445 | try depTokenizer("$$foo.o:", "target = {$foo.o}"); | ||
| 446 | } | ||
| 447 | |||
| 448 | test "empty target linefeeds" { | ||
| 449 | try depTokenizer("\n", ""); | ||
| 450 | try depTokenizer("\r\n", ""); | ||
| 451 | |||
| 452 | const expect = "target = {foo.o}"; | ||
| 453 | try depTokenizer( | ||
| 454 | \\foo.o: | ||
| 455 | , expect); | ||
| 456 | try depTokenizer( | ||
| 457 | \\foo.o: | ||
| 458 | \\ | ||
| 459 | , expect); | ||
| 460 | try depTokenizer( | ||
| 461 | \\foo.o: | ||
| 462 | , expect); | ||
| 463 | try depTokenizer( | ||
| 464 | \\foo.o: | ||
| 465 | \\ | ||
| 466 | , expect); | ||
| 467 | } | ||
| 468 | |||
| 469 | test "empty target linefeeds + continuations" { | ||
| 470 | const expect = "target = {foo.o}"; | ||
| 471 | try depTokenizer( | ||
| 472 | \\foo.o:\ | ||
| 473 | , expect); | ||
| 474 | try depTokenizer( | ||
| 475 | \\foo.o:\ | ||
| 476 | \\ | ||
| 477 | , expect); | ||
| 478 | try depTokenizer( | ||
| 479 | \\foo.o:\ | ||
| 480 | , expect); | ||
| 481 | try depTokenizer( | ||
| 482 | \\foo.o:\ | ||
| 483 | \\ | ||
| 484 | , expect); | ||
| 485 | } | ||
| 486 | |||
| 487 | test "empty target linefeeds + hspace + continuations" { | ||
| 488 | const expect = "target = {foo.o}"; | ||
| 489 | try depTokenizer( | ||
| 490 | \\foo.o: \ | ||
| 491 | , expect); | ||
| 492 | try depTokenizer( | ||
| 493 | \\foo.o: \ | ||
| 494 | \\ | ||
| 495 | , expect); | ||
| 496 | try depTokenizer( | ||
| 497 | \\foo.o: \ | ||
| 498 | , expect); | ||
| 499 | try depTokenizer( | ||
| 500 | \\foo.o: \ | ||
| 501 | \\ | ||
| 502 | , expect); | ||
| 503 | } | ||
| 504 | |||
| 505 | test "prereq" { | ||
| 506 | const expect = | ||
| 507 | \\target = {foo.o} | ||
| 508 | \\prereq = {foo.c} | ||
| 509 | ; | ||
| 510 | try depTokenizer("foo.o: foo.c", expect); | ||
| 511 | try depTokenizer( | ||
| 512 | \\foo.o: \ | ||
| 513 | \\foo.c | ||
| 514 | , expect); | ||
| 515 | try depTokenizer( | ||
| 516 | \\foo.o: \ | ||
| 517 | \\ foo.c | ||
| 518 | , expect); | ||
| 519 | try depTokenizer( | ||
| 520 | \\foo.o: \ | ||
| 521 | \\ foo.c | ||
| 522 | , expect); | ||
| 523 | } | ||
| 524 | |||
| 525 | test "prereq continuation" { | ||
| 526 | const expect = | ||
| 527 | \\target = {foo.o} | ||
| 528 | \\prereq = {foo.h} | ||
| 529 | \\prereq = {bar.h} | ||
| 530 | ; | ||
| 531 | try depTokenizer( | ||
| 532 | \\foo.o: foo.h\ | ||
| 533 | \\bar.h | ||
| 534 | , expect); | ||
| 535 | try depTokenizer( | ||
| 536 | \\foo.o: foo.h\ | ||
| 537 | \\bar.h | ||
| 538 | , expect); | ||
| 539 | } | ||
| 540 | |||
| 541 | test "multiple prereqs" { | ||
| 542 | const expect = | ||
| 543 | \\target = {foo.o} | ||
| 544 | \\prereq = {foo.c} | ||
| 545 | \\prereq = {foo.h} | ||
| 546 | \\prereq = {bar.h} | ||
| 547 | ; | ||
| 548 | try depTokenizer("foo.o: foo.c foo.h bar.h", expect); | ||
| 549 | try depTokenizer( | ||
| 550 | \\foo.o: \ | ||
| 551 | \\foo.c foo.h bar.h | ||
| 552 | , expect); | ||
| 553 | try depTokenizer( | ||
| 554 | \\foo.o: foo.c foo.h bar.h\ | ||
| 555 | , expect); | ||
| 556 | try depTokenizer( | ||
| 557 | \\foo.o: foo.c foo.h bar.h\ | ||
| 558 | \\ | ||
| 559 | , expect); | ||
| 560 | try depTokenizer( | ||
| 561 | \\foo.o: \ | ||
| 562 | \\foo.c \ | ||
| 563 | \\ foo.h\ | ||
| 564 | \\bar.h | ||
| 565 | \\ | ||
| 566 | , expect); | ||
| 567 | try depTokenizer( | ||
| 568 | \\foo.o: \ | ||
| 569 | \\foo.c \ | ||
| 570 | \\ foo.h\ | ||
| 571 | \\bar.h\ | ||
| 572 | \\ | ||
| 573 | , expect); | ||
| 574 | try depTokenizer( | ||
| 575 | \\foo.o: \ | ||
| 576 | \\foo.c \ | ||
| 577 | \\ foo.h\ | ||
| 578 | \\bar.h\ | ||
| 579 | , expect); | ||
| 580 | } | ||
| 581 | |||
| 582 | test "multiple targets and prereqs" { | ||
| 583 | try depTokenizer( | ||
| 584 | \\foo.o: foo.c | ||
| 585 | \\bar.o: bar.c a.h b.h c.h | ||
| 586 | \\abc.o: abc.c \ | ||
| 587 | \\ one.h two.h \ | ||
| 588 | \\ three.h four.h | ||
| 589 | , | ||
| 590 | \\target = {foo.o} | ||
| 591 | \\prereq = {foo.c} | ||
| 592 | \\target = {bar.o} | ||
| 593 | \\prereq = {bar.c} | ||
| 594 | \\prereq = {a.h} | ||
| 595 | \\prereq = {b.h} | ||
| 596 | \\prereq = {c.h} | ||
| 597 | \\target = {abc.o} | ||
| 598 | \\prereq = {abc.c} | ||
| 599 | \\prereq = {one.h} | ||
| 600 | \\prereq = {two.h} | ||
| 601 | \\prereq = {three.h} | ||
| 602 | \\prereq = {four.h} | ||
| 603 | ); | ||
| 604 | try depTokenizer( | ||
| 605 | \\ascii.o: ascii.c | ||
| 606 | \\base64.o: base64.c stdio.h | ||
| 607 | \\elf.o: elf.c a.h b.h c.h | ||
| 608 | \\macho.o: \ | ||
| 609 | \\ macho.c\ | ||
| 610 | \\ a.h b.h c.h | ||
| 611 | , | ||
| 612 | \\target = {ascii.o} | ||
| 613 | \\prereq = {ascii.c} | ||
| 614 | \\target = {base64.o} | ||
| 615 | \\prereq = {base64.c} | ||
| 616 | \\prereq = {stdio.h} | ||
| 617 | \\target = {elf.o} | ||
| 618 | \\prereq = {elf.c} | ||
| 619 | \\prereq = {a.h} | ||
| 620 | \\prereq = {b.h} | ||
| 621 | \\prereq = {c.h} | ||
| 622 | \\target = {macho.o} | ||
| 623 | \\prereq = {macho.c} | ||
| 624 | \\prereq = {a.h} | ||
| 625 | \\prereq = {b.h} | ||
| 626 | \\prereq = {c.h} | ||
| 627 | ); | ||
| 628 | try depTokenizer( | ||
| 629 | \\a$$scii.o: ascii.c | ||
| 630 | \\\\base64.o: "\base64.c" "s t#dio.h" | ||
| 631 | \\e\\lf.o: "e\lf.c" "a.h$$" "$$b.h c.h$$" | ||
| 632 | \\macho.o: \ | ||
| 633 | \\ "macho!.c" \ | ||
| 634 | \\ a.h b.h c.h | ||
| 635 | , | ||
| 636 | \\target = {a$scii.o} | ||
| 637 | \\prereq = {ascii.c} | ||
| 638 | \\target = {\base64.o} | ||
| 639 | \\prereq = {\base64.c} | ||
| 640 | \\prereq = {s t#dio.h} | ||
| 641 | \\target = {e\lf.o} | ||
| 642 | \\prereq = {e\lf.c} | ||
| 643 | \\prereq = {a.h$$} | ||
| 644 | \\prereq = {$$b.h c.h$$} | ||
| 645 | \\target = {macho.o} | ||
| 646 | \\prereq = {macho!.c} | ||
| 647 | \\prereq = {a.h} | ||
| 648 | \\prereq = {b.h} | ||
| 649 | \\prereq = {c.h} | ||
| 650 | ); | ||
| 651 | } | ||
| 652 | |||
| 653 | test "windows quoted prereqs" { | ||
| 654 | try depTokenizer( | ||
| 655 | \\c:\foo.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo.c" | ||
| 656 | \\c:\foo2.o: "C:\Program Files (x86)\Microsoft Visual Studio\foo2.c" \ | ||
| 657 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo1.h" \ | ||
| 658 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\foo2.h" | ||
| 659 | , | ||
| 660 | \\target = {c:\foo.o} | ||
| 661 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo.c} | ||
| 662 | \\target = {c:\foo2.o} | ||
| 663 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.c} | ||
| 664 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo1.h} | ||
| 665 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\foo2.h} | ||
| 666 | ); | ||
| 667 | } | ||
| 668 | |||
| 669 | test "windows mixed prereqs" { | ||
| 670 | try depTokenizer( | ||
| 671 | \\cimport.o: \ | ||
| 672 | \\ C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h \ | ||
| 673 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h" \ | ||
| 674 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h" \ | ||
| 675 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h" \ | ||
| 676 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h" \ | ||
| 677 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h" \ | ||
| 678 | \\ C:\msys64\opt\zig\lib\zig\include\vadefs.h \ | ||
| 679 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h" \ | ||
| 680 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h" \ | ||
| 681 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h" \ | ||
| 682 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h" \ | ||
| 683 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h" \ | ||
| 684 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h" \ | ||
| 685 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h" \ | ||
| 686 | \\ "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h" \ | ||
| 687 | \\ "C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h" | ||
| 688 | , | ||
| 689 | \\target = {cimport.o} | ||
| 690 | \\prereq = {C:\msys64\home\anon\project\zig\master\zig-cache\o\qhvhbUo7GU5iKyQ5mpA8TcQpncCYaQu0wwvr3ybiSTj_Dtqi1Nmcb70kfODJ2Qlg\cimport.h} | ||
| 691 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\stdio.h} | ||
| 692 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt.h} | ||
| 693 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime.h} | ||
| 694 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\sal.h} | ||
| 695 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\concurrencysal.h} | ||
| 696 | \\prereq = {C:\msys64\opt\zig\lib\zig\include\vadefs.h} | ||
| 697 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vadefs.h} | ||
| 698 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstdio.h} | ||
| 699 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_stdio_config.h} | ||
| 700 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\string.h} | ||
| 701 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memory.h} | ||
| 702 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_memcpy_s.h} | ||
| 703 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\errno.h} | ||
| 704 | \\prereq = {C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.21.27702\lib\x64\\..\..\include\vcruntime_string.h} | ||
| 705 | \\prereq = {C:\Program Files (x86)\Windows Kits\10\\Include\10.0.17763.0\ucrt\corecrt_wstring.h} | ||
| 706 | ); | ||
| 707 | } | ||
| 708 | |||
| 709 | test "windows funky targets" { | ||
| 710 | try depTokenizer( | ||
| 711 | \\C:\Users\anon\foo.o: | ||
| 712 | \\C:\Users\anon\foo\ .o: | ||
| 713 | \\C:\Users\anon\foo\#.o: | ||
| 714 | \\C:\Users\anon\foo$$.o: | ||
| 715 | \\C:\Users\anon\\\ foo.o: | ||
| 716 | \\C:\Users\anon\\#foo.o: | ||
| 717 | \\C:\Users\anon\$$foo.o: | ||
| 718 | \\C:\Users\anon\\\ \ \ \ \ foo.o: | ||
| 719 | , | ||
| 720 | \\target = {C:\Users\anon\foo.o} | ||
| 721 | \\target = {C:\Users\anon\foo .o} | ||
| 722 | \\target = {C:\Users\anon\foo#.o} | ||
| 723 | \\target = {C:\Users\anon\foo$.o} | ||
| 724 | \\target = {C:\Users\anon\ foo.o} | ||
| 725 | \\target = {C:\Users\anon\#foo.o} | ||
| 726 | \\target = {C:\Users\anon\$foo.o} | ||
| 727 | \\target = {C:\Users\anon\ foo.o} | ||
| 728 | ); | ||
| 729 | } | ||
| 730 | |||
| 731 | test "windows drive and forward slashes" { | ||
| 732 | try depTokenizer( | ||
| 733 | \\C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj: \ | ||
| 734 | \\ C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c | ||
| 735 | , | ||
| 736 | \\target = {C:/msys64/what/zig-cache\tmp\48ac4d78dd531abd-cxa_thread_atexit.obj} | ||
| 737 | \\prereq = {C:/msys64/opt/zig3/lib/zig/libc/mingw/crt/cxa_thread_atexit.c} | ||
| 738 | ); | ||
| 739 | } | ||
| 740 | |||
| 741 | test "error incomplete escape - reverse_solidus" { | ||
| 742 | try depTokenizer("\\", | ||
| 743 | \\ERROR: illegal char '\' at position 0: incomplete escape | ||
| 744 | ); | ||
| 745 | try depTokenizer("\t\\", | ||
| 746 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 747 | ); | ||
| 748 | try depTokenizer("\n\\", | ||
| 749 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 750 | ); | ||
| 751 | try depTokenizer("\r\\", | ||
| 752 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 753 | ); | ||
| 754 | try depTokenizer("\r\n\\", | ||
| 755 | \\ERROR: illegal char '\' at position 2: incomplete escape | ||
| 756 | ); | ||
| 757 | try depTokenizer(" \\", | ||
| 758 | \\ERROR: illegal char '\' at position 1: incomplete escape | ||
| 759 | ); | ||
| 760 | } | ||
| 761 | |||
| 762 | test "error incomplete escape - dollar_sign" { | ||
| 763 | try depTokenizer("$", | ||
| 764 | \\ERROR: illegal char '$' at position 0: incomplete escape | ||
| 765 | ); | ||
| 766 | try depTokenizer("\t$", | ||
| 767 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 768 | ); | ||
| 769 | try depTokenizer("\n$", | ||
| 770 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 771 | ); | ||
| 772 | try depTokenizer("\r$", | ||
| 773 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 774 | ); | ||
| 775 | try depTokenizer("\r\n$", | ||
| 776 | \\ERROR: illegal char '$' at position 2: incomplete escape | ||
| 777 | ); | ||
| 778 | try depTokenizer(" $", | ||
| 779 | \\ERROR: illegal char '$' at position 1: incomplete escape | ||
| 780 | ); | ||
| 781 | } | ||
| 782 | |||
| 783 | test "error incomplete target" { | ||
| 784 | try depTokenizer("foo.o", | ||
| 785 | \\ERROR: incomplete target 'foo.o' at position 0 | ||
| 786 | ); | ||
| 787 | try depTokenizer("\tfoo.o", | ||
| 788 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 789 | ); | ||
| 790 | try depTokenizer("\nfoo.o", | ||
| 791 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 792 | ); | ||
| 793 | try depTokenizer("\rfoo.o", | ||
| 794 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 795 | ); | ||
| 796 | try depTokenizer("\r\nfoo.o", | ||
| 797 | \\ERROR: incomplete target 'foo.o' at position 2 | ||
| 798 | ); | ||
| 799 | try depTokenizer(" foo.o", | ||
| 800 | \\ERROR: incomplete target 'foo.o' at position 1 | ||
| 801 | ); | ||
| 802 | |||
| 803 | try depTokenizer("\\ foo.o", | ||
| 804 | \\ERROR: incomplete target ' foo.o' at position 0 | ||
| 805 | ); | ||
| 806 | try depTokenizer("\\#foo.o", | ||
| 807 | \\ERROR: incomplete target '#foo.o' at position 0 | ||
| 808 | ); | ||
| 809 | try depTokenizer("\\\\foo.o", | ||
| 810 | \\ERROR: incomplete target '\foo.o' at position 0 | ||
| 811 | ); | ||
| 812 | try depTokenizer("$$foo.o", | ||
| 813 | \\ERROR: incomplete target '$foo.o' at position 0 | ||
| 814 | ); | ||
| 815 | } | ||
| 816 | |||
| 817 | test "error illegal char at position - bad target escape" { | ||
| 818 | try depTokenizer("\\\t", | ||
| 819 | \\ERROR: illegal char \x09 at position 1: bad target escape | ||
| 820 | ); | ||
| 821 | try depTokenizer("\\\n", | ||
| 822 | \\ERROR: illegal char \x0A at position 1: bad target escape | ||
| 823 | ); | ||
| 824 | try depTokenizer("\\\r", | ||
| 825 | \\ERROR: illegal char \x0D at position 1: bad target escape | ||
| 826 | ); | ||
| 827 | try depTokenizer("\\\r\n", | ||
| 828 | \\ERROR: illegal char \x0D at position 1: bad target escape | ||
| 829 | ); | ||
| 830 | } | ||
| 831 | |||
| 832 | test "error illegal char at position - execting dollar_sign" { | ||
| 833 | try depTokenizer("$\t", | ||
| 834 | \\ERROR: illegal char \x09 at position 1: expecting '$' | ||
| 835 | ); | ||
| 836 | try depTokenizer("$\n", | ||
| 837 | \\ERROR: illegal char \x0A at position 1: expecting '$' | ||
| 838 | ); | ||
| 839 | try depTokenizer("$\r", | ||
| 840 | \\ERROR: illegal char \x0D at position 1: expecting '$' | ||
| 841 | ); | ||
| 842 | try depTokenizer("$\r\n", | ||
| 843 | \\ERROR: illegal char \x0D at position 1: expecting '$' | ||
| 844 | ); | ||
| 845 | } | ||
| 846 | |||
| 847 | test "error illegal char at position - invalid target" { | ||
| 848 | try depTokenizer("foo\t.o", | ||
| 849 | \\ERROR: illegal char \x09 at position 3: invalid target | ||
| 850 | ); | ||
| 851 | try depTokenizer("foo\n.o", | ||
| 852 | \\ERROR: illegal char \x0A at position 3: invalid target | ||
| 853 | ); | ||
| 854 | try depTokenizer("foo\r.o", | ||
| 855 | \\ERROR: illegal char \x0D at position 3: invalid target | ||
| 856 | ); | ||
| 857 | try depTokenizer("foo\r\n.o", | ||
| 858 | \\ERROR: illegal char \x0D at position 3: invalid target | ||
| 859 | ); | ||
| 860 | } | ||
| 861 | |||
| 862 | test "error target - continuation expecting end-of-line" { | ||
| 863 | try depTokenizer("foo.o: \\\t", | ||
| 864 | \\target = {foo.o} | ||
| 865 | \\ERROR: illegal char \x09 at position 8: continuation expecting end-of-line | ||
| 866 | ); | ||
| 867 | try depTokenizer("foo.o: \\ ", | ||
| 868 | \\target = {foo.o} | ||
| 869 | \\ERROR: illegal char ' ' at position 8: continuation expecting end-of-line | ||
| 870 | ); | ||
| 871 | try depTokenizer("foo.o: \\x", | ||
| 872 | \\target = {foo.o} | ||
| 873 | \\ERROR: illegal char 'x' at position 8: continuation expecting end-of-line | ||
| 874 | ); | ||
| 875 | try depTokenizer("foo.o: \\\x0dx", | ||
| 876 | \\target = {foo.o} | ||
| 877 | \\ERROR: illegal char 'x' at position 9: continuation expecting end-of-line | ||
| 878 | ); | ||
| 879 | } | ||
| 880 | |||
| 881 | test "error prereq - continuation expecting end-of-line" { | ||
| 882 | try depTokenizer("foo.o: foo.h\\\x0dx", | ||
| 883 | \\target = {foo.o} | ||
| 884 | \\ERROR: illegal char 'x' at position 14: continuation expecting end-of-line | ||
| 885 | ); | ||
| 886 | } | ||
| 887 | |||
| 888 | // - tokenize input, emit textual representation, and compare to expect | ||
| 889 | fn depTokenizer(input: []const u8, expect: []const u8) !void { | ||
| 890 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); | ||
| 891 | const arena = arena_allocator.allocator(); | ||
| 892 | defer arena_allocator.deinit(); | ||
| 893 | |||
| 894 | var it: Tokenizer = .{ .bytes = input }; | ||
| 895 | var buffer = std.ArrayList(u8).init(arena); | ||
| 896 | var resolve_buf = std.ArrayList(u8).init(arena); | ||
| 897 | var i: usize = 0; | ||
| 898 | while (it.next()) |token| { | ||
| 899 | if (i != 0) try buffer.appendSlice("\n"); | ||
| 900 | switch (token) { | ||
| 901 | .target, .prereq => |bytes| { | ||
| 902 | try buffer.appendSlice(@tagName(token)); | ||
| 903 | try buffer.appendSlice(" = {"); | ||
| 904 | for (bytes) |b| { | ||
| 905 | try buffer.append(printable_char_tab[b]); | ||
| 906 | } | ||
| 907 | try buffer.appendSlice("}"); | ||
| 908 | }, | ||
| 909 | .target_must_resolve => { | ||
| 910 | try buffer.appendSlice("target = {"); | ||
| 911 | try token.resolve(resolve_buf.writer()); | ||
| 912 | for (resolve_buf.items) |b| { | ||
| 913 | try buffer.append(printable_char_tab[b]); | ||
| 914 | } | ||
| 915 | resolve_buf.items.len = 0; | ||
| 916 | try buffer.appendSlice("}"); | ||
| 917 | }, | ||
| 918 | else => { | ||
| 919 | try buffer.appendSlice("ERROR: "); | ||
| 920 | try token.printError(buffer.writer()); | ||
| 921 | break; | ||
| 922 | }, | ||
| 923 | } | ||
| 924 | i += 1; | ||
| 925 | } | ||
| 926 | |||
| 927 | if (std.mem.eql(u8, expect, buffer.items)) { | ||
| 928 | try testing.expect(true); | ||
| 929 | return; | ||
| 930 | } | ||
| 931 | |||
| 932 | const out = std.io.getStdErr().writer(); | ||
| 933 | |||
| 934 | try out.writeAll("\n"); | ||
| 935 | try printSection(out, "<<<< input", input); | ||
| 936 | try printSection(out, "==== expect", expect); | ||
| 937 | try printSection(out, ">>>> got", buffer.items); | ||
| 938 | try printRuler(out); | ||
| 939 | |||
| 940 | try testing.expect(false); | ||
| 941 | } | ||
| 942 | |||
| 943 | fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void { | ||
| 944 | try printLabel(out, label, bytes); | ||
| 945 | try hexDump(out, bytes); | ||
| 946 | try printRuler(out); | ||
| 947 | try out.writeAll(bytes); | ||
| 948 | try out.writeAll("\n"); | ||
| 949 | } | ||
| 950 | |||
| 951 | fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void { | ||
| 952 | var buf: [80]u8 = undefined; | ||
| 953 | var text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len }); | ||
| 954 | try out.writeAll(text); | ||
| 955 | var i: usize = text.len; | ||
| 956 | const end = 79; | ||
| 957 | while (i < end) : (i += 1) { | ||
| 958 | try out.writeAll(&[_]u8{label[0]}); | ||
| 959 | } | ||
| 960 | try out.writeAll("\n"); | ||
| 961 | } | ||
| 962 | |||
| 963 | fn printRuler(out: anytype) !void { | ||
| 964 | var i: usize = 0; | ||
| 965 | const end = 79; | ||
| 966 | while (i < end) : (i += 1) { | ||
| 967 | try out.writeAll("-"); | ||
| 968 | } | ||
| 969 | try out.writeAll("\n"); | ||
| 970 | } | ||
| 971 | |||
| 972 | fn hexDump(out: anytype, bytes: []const u8) !void { | ||
| 973 | const n16 = bytes.len >> 4; | ||
| 974 | var line: usize = 0; | ||
| 975 | var offset: usize = 0; | ||
| 976 | while (line < n16) : (line += 1) { | ||
| 977 | try hexDump16(out, offset, bytes[offset .. offset + 16]); | ||
| 978 | offset += 16; | ||
| 979 | } | ||
| 980 | |||
| 981 | const n = bytes.len & 0x0f; | ||
| 982 | if (n > 0) { | ||
| 983 | try printDecValue(out, offset, 8); | ||
| 984 | try out.writeAll(":"); | ||
| 985 | try out.writeAll(" "); | ||
| 986 | var end1 = std.math.min(offset + n, offset + 8); | ||
| 987 | for (bytes[offset..end1]) |b| { | ||
| 988 | try out.writeAll(" "); | ||
| 989 | try printHexValue(out, b, 2); | ||
| 990 | } | ||
| 991 | var end2 = offset + n; | ||
| 992 | if (end2 > end1) { | ||
| 993 | try out.writeAll(" "); | ||
| 994 | for (bytes[end1..end2]) |b| { | ||
| 995 | try out.writeAll(" "); | ||
| 996 | try printHexValue(out, b, 2); | ||
| 997 | } | ||
| 998 | } | ||
| 999 | const short = 16 - n; | ||
| 1000 | var i: usize = 0; | ||
| 1001 | while (i < short) : (i += 1) { | ||
| 1002 | try out.writeAll(" "); | ||
| 1003 | } | ||
| 1004 | if (end2 > end1) { | ||
| 1005 | try out.writeAll(" |"); | ||
| 1006 | } else { | ||
| 1007 | try out.writeAll(" |"); | ||
| 1008 | } | ||
| 1009 | try printCharValues(out, bytes[offset..end2]); | ||
| 1010 | try out.writeAll("|\n"); | ||
| 1011 | offset += n; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | try printDecValue(out, offset, 8); | ||
| 1015 | try out.writeAll(":"); | ||
| 1016 | try out.writeAll("\n"); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | fn hexDump16(out: anytype, offset: usize, bytes: []const u8) !void { | ||
| 1020 | try printDecValue(out, offset, 8); | ||
| 1021 | try out.writeAll(":"); | ||
| 1022 | try out.writeAll(" "); | ||
| 1023 | for (bytes[0..8]) |b| { | ||
| 1024 | try out.writeAll(" "); | ||
| 1025 | try printHexValue(out, b, 2); | ||
| 1026 | } | ||
| 1027 | try out.writeAll(" "); | ||
| 1028 | for (bytes[8..16]) |b| { | ||
| 1029 | try out.writeAll(" "); | ||
| 1030 | try printHexValue(out, b, 2); | ||
| 1031 | } | ||
| 1032 | try out.writeAll(" |"); | ||
| 1033 | try printCharValues(out, bytes); | ||
| 1034 | try out.writeAll("|\n"); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | fn printDecValue(out: anytype, value: u64, width: u8) !void { | ||
| 1038 | var buffer: [20]u8 = undefined; | ||
| 1039 | const len = std.fmt.formatIntBuf(buffer[0..], value, 10, .lower, .{ .width = width, .fill = '0' }); | ||
| 1040 | try out.writeAll(buffer[0..len]); | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | fn printHexValue(out: anytype, value: u64, width: u8) !void { | ||
| 1044 | var buffer: [16]u8 = undefined; | ||
| 1045 | const len = std.fmt.formatIntBuf(buffer[0..], value, 16, .lower, .{ .width = width, .fill = '0' }); | ||
| 1046 | try out.writeAll(buffer[0..len]); | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | fn printCharValues(out: anytype, bytes: []const u8) !void { | ||
| 1050 | for (bytes) |b| { | ||
| 1051 | try out.writeAll(&[_]u8{printable_char_tab[b]}); | ||
| 1052 | } | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | fn printUnderstandableChar(out: anytype, char: u8) !void { | ||
| 1056 | if (std.ascii.isPrint(char)) { | ||
| 1057 | try out.print("'{c}'", .{char}); | ||
| 1058 | } else { | ||
| 1059 | try out.print("\\x{X:0>2}", .{char}); | ||
| 1060 | } | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | // zig fmt: off | ||
| 1064 | const printable_char_tab: [256]u8 = ( | ||
| 1065 | "................................ !\"#$%&'()*+,-./0123456789:;<=>?" ++ | ||
| 1066 | "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~." ++ | ||
| 1067 | "................................................................" ++ | ||
| 1068 | "................................................................" | ||
| 1069 | ).*; | ||
src/Module.zig+41-15| ... | @@ -16,7 +16,7 @@ const Ast = std.zig.Ast; | ... | @@ -16,7 +16,7 @@ const Ast = std.zig.Ast; |
| 16 | 16 | ||
| 17 | const Module = @This(); | 17 | const Module = @This(); |
| 18 | const Compilation = @import("Compilation.zig"); | 18 | const Compilation = @import("Compilation.zig"); |
| 19 | const Cache = @import("Cache.zig"); | 19 | const Cache = std.Build.Cache; |
| 20 | const Value = @import("value.zig").Value; | 20 | const Value = @import("value.zig").Value; |
| 21 | const Type = @import("type.zig").Type; | 21 | const Type = @import("type.zig").Type; |
| 22 | const TypedValue = @import("TypedValue.zig"); | 22 | const TypedValue = @import("TypedValue.zig"); |
| ... | @@ -4865,14 +4865,31 @@ pub fn importFile( | ... | @@ -4865,14 +4865,31 @@ pub fn importFile( |
| 4865 | }; | 4865 | }; |
| 4866 | } | 4866 | } |
| 4867 | 4867 | ||
| 4868 | pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*EmbedFile { | 4868 | pub fn embedFile(mod: *Module, cur_file: *File, import_string: []const u8) !*EmbedFile { |
| 4869 | const gpa = mod.gpa; | 4869 | const gpa = mod.gpa; |
| 4870 | 4870 | ||
| 4871 | // The resolved path is used as the key in the table, to detect if | 4871 | if (cur_file.pkg.table.get(import_string)) |pkg| { |
| 4872 | // a file refers to the same as another, despite different relative paths. | 4872 | const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{ |
| 4873 | pkg.root_src_directory.path orelse ".", pkg.root_src_path, | ||
| 4874 | }); | ||
| 4875 | var keep_resolved_path = false; | ||
| 4876 | defer if (!keep_resolved_path) gpa.free(resolved_path); | ||
| 4877 | |||
| 4878 | const gop = try mod.embed_table.getOrPut(gpa, resolved_path); | ||
| 4879 | errdefer assert(mod.embed_table.remove(resolved_path)); | ||
| 4880 | if (gop.found_existing) return gop.value_ptr.*; | ||
| 4881 | |||
| 4882 | const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); | ||
| 4883 | errdefer gpa.free(sub_file_path); | ||
| 4884 | |||
| 4885 | return newEmbedFile(mod, pkg, sub_file_path, resolved_path, &keep_resolved_path, gop); | ||
| 4886 | } | ||
| 4887 | |||
| 4888 | // The resolved path is used as the key in the table, to detect if a file | ||
| 4889 | // refers to the same as another, despite different relative paths. | ||
| 4873 | const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse "."; | 4890 | const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse "."; |
| 4874 | const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{ | 4891 | const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{ |
| 4875 | cur_pkg_dir_path, cur_file.sub_file_path, "..", rel_file_path, | 4892 | cur_pkg_dir_path, cur_file.sub_file_path, "..", import_string, |
| 4876 | }); | 4893 | }); |
| 4877 | var keep_resolved_path = false; | 4894 | var keep_resolved_path = false; |
| 4878 | defer if (!keep_resolved_path) gpa.free(resolved_path); | 4895 | defer if (!keep_resolved_path) gpa.free(resolved_path); |
| ... | @@ -4881,9 +4898,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb | ... | @@ -4881,9 +4898,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb |
| 4881 | errdefer assert(mod.embed_table.remove(resolved_path)); | 4898 | errdefer assert(mod.embed_table.remove(resolved_path)); |
| 4882 | if (gop.found_existing) return gop.value_ptr.*; | 4899 | if (gop.found_existing) return gop.value_ptr.*; |
| 4883 | 4900 | ||
| 4884 | const new_file = try gpa.create(EmbedFile); | ||
| 4885 | errdefer gpa.destroy(new_file); | ||
| 4886 | |||
| 4887 | const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path}); | 4901 | const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path}); |
| 4888 | defer gpa.free(resolved_root_path); | 4902 | defer gpa.free(resolved_root_path); |
| 4889 | 4903 | ||
| ... | @@ -4902,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb | ... | @@ -4902,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb |
| 4902 | }; | 4916 | }; |
| 4903 | errdefer gpa.free(sub_file_path); | 4917 | errdefer gpa.free(sub_file_path); |
| 4904 | 4918 | ||
| 4905 | var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{}); | 4919 | return newEmbedFile(mod, cur_file.pkg, sub_file_path, resolved_path, &keep_resolved_path, gop); |
| 4920 | } | ||
| 4921 | |||
| 4922 | fn newEmbedFile( | ||
| 4923 | mod: *Module, | ||
| 4924 | pkg: *Package, | ||
| 4925 | sub_file_path: []const u8, | ||
| 4926 | resolved_path: []const u8, | ||
| 4927 | keep_resolved_path: *bool, | ||
| 4928 | gop: std.StringHashMapUnmanaged(*EmbedFile).GetOrPutResult, | ||
| 4929 | ) !*EmbedFile { | ||
| 4930 | const gpa = mod.gpa; | ||
| 4931 | |||
| 4932 | const new_file = try gpa.create(EmbedFile); | ||
| 4933 | errdefer gpa.destroy(new_file); | ||
| 4934 | |||
| 4935 | var file = try pkg.root_src_directory.handle.openFile(sub_file_path, .{}); | ||
| 4906 | defer file.close(); | 4936 | defer file.close(); |
| 4907 | 4937 | ||
| 4908 | const actual_stat = try file.stat(); | 4938 | const actual_stat = try file.stat(); |
| ... | @@ -4915,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb | ... | @@ -4915,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb |
| 4915 | const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0); | 4945 | const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0); |
| 4916 | errdefer gpa.free(bytes); | 4946 | errdefer gpa.free(bytes); |
| 4917 | 4947 | ||
| 4918 | log.debug("new embedFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, rel_file_path={s}", .{ | ||
| 4919 | resolved_root_path, resolved_path, sub_file_path, rel_file_path, | ||
| 4920 | }); | ||
| 4921 | |||
| 4922 | if (mod.comp.whole_cache_manifest) |whole_cache_manifest| { | 4948 | if (mod.comp.whole_cache_manifest) |whole_cache_manifest| { |
| 4923 | const copied_resolved_path = try gpa.dupe(u8, resolved_path); | 4949 | const copied_resolved_path = try gpa.dupe(u8, resolved_path); |
| 4924 | errdefer gpa.free(copied_resolved_path); | 4950 | errdefer gpa.free(copied_resolved_path); |
| ... | @@ -4927,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb | ... | @@ -4927,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb |
| 4927 | try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat); | 4953 | try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat); |
| 4928 | } | 4954 | } |
| 4929 | 4955 | ||
| 4930 | keep_resolved_path = true; // It's now owned by embed_table. | 4956 | keep_resolved_path.* = true; // It's now owned by embed_table. |
| 4931 | gop.value_ptr.* = new_file; | 4957 | gop.value_ptr.* = new_file; |
| 4932 | new_file.* = .{ | 4958 | new_file.* = .{ |
| 4933 | .sub_file_path = sub_file_path, | 4959 | .sub_file_path = sub_file_path, |
| 4934 | .bytes = bytes, | 4960 | .bytes = bytes, |
| 4935 | .stat = stat, | 4961 | .stat = stat, |
| 4936 | .pkg = cur_file.pkg, | 4962 | .pkg = pkg, |
| 4937 | .owner_decl = undefined, // Set by Sema immediately after this function returns. | 4963 | .owner_decl = undefined, // Set by Sema immediately after this function returns. |
| 4938 | }; | 4964 | }; |
| 4939 | return new_file; | 4965 | return new_file; |
src/Package.zig+1-1| ... | @@ -13,7 +13,7 @@ const Compilation = @import("Compilation.zig"); | ... | @@ -13,7 +13,7 @@ const Compilation = @import("Compilation.zig"); |
| 13 | const Module = @import("Module.zig"); | 13 | const Module = @import("Module.zig"); |
| 14 | const ThreadPool = @import("ThreadPool.zig"); | 14 | const ThreadPool = @import("ThreadPool.zig"); |
| 15 | const WaitGroup = @import("WaitGroup.zig"); | 15 | const WaitGroup = @import("WaitGroup.zig"); |
| 16 | const Cache = @import("Cache.zig"); | 16 | const Cache = std.Build.Cache; |
| 17 | const build_options = @import("build_options"); | 17 | const build_options = @import("build_options"); |
| 18 | const Manifest = @import("Manifest.zig"); | 18 | const Manifest = @import("Manifest.zig"); |
| 19 | 19 |
src/Sema.zig+66-44| ... | @@ -1015,7 +1015,6 @@ fn analyzeBodyInner( | ... | @@ -1015,7 +1015,6 @@ fn analyzeBodyInner( |
| 1015 | .float_cast => try sema.zirFloatCast(block, inst), | 1015 | .float_cast => try sema.zirFloatCast(block, inst), |
| 1016 | .int_cast => try sema.zirIntCast(block, inst), | 1016 | .int_cast => try sema.zirIntCast(block, inst), |
| 1017 | .ptr_cast => try sema.zirPtrCast(block, inst), | 1017 | .ptr_cast => try sema.zirPtrCast(block, inst), |
| 1018 | .qual_cast => try sema.zirQualCast(block, inst), | ||
| 1019 | .truncate => try sema.zirTruncate(block, inst), | 1018 | .truncate => try sema.zirTruncate(block, inst), |
| 1020 | .align_cast => try sema.zirAlignCast(block, inst), | 1019 | .align_cast => try sema.zirAlignCast(block, inst), |
| 1021 | .has_decl => try sema.zirHasDecl(block, inst), | 1020 | .has_decl => try sema.zirHasDecl(block, inst), |
| ... | @@ -1147,6 +1146,8 @@ fn analyzeBodyInner( | ... | @@ -1147,6 +1146,8 @@ fn analyzeBodyInner( |
| 1147 | .c_va_copy => try sema.zirCVaCopy( block, extended), | 1146 | .c_va_copy => try sema.zirCVaCopy( block, extended), |
| 1148 | .c_va_end => try sema.zirCVaEnd( block, extended), | 1147 | .c_va_end => try sema.zirCVaEnd( block, extended), |
| 1149 | .c_va_start => try sema.zirCVaStart( block, extended), | 1148 | .c_va_start => try sema.zirCVaStart( block, extended), |
| 1149 | .const_cast, => try sema.zirConstCast( block, extended), | ||
| 1150 | .volatile_cast, => try sema.zirVolatileCast( block, extended), | ||
| 1150 | // zig fmt: on | 1151 | // zig fmt: on |
| 1151 | 1152 | ||
| 1152 | .fence => { | 1153 | .fence => { |
| ... | @@ -7669,17 +7670,21 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -7669,17 +7670,21 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr |
| 7669 | error_set.fmt(sema.mod), | 7670 | error_set.fmt(sema.mod), |
| 7670 | }); | 7671 | }); |
| 7671 | } | 7672 | } |
| 7672 | if (payload.zigTypeTag() == .Opaque) { | 7673 | try sema.validateErrorUnionPayloadType(block, payload, rhs_src); |
| 7673 | return sema.fail(block, rhs_src, "error union with payload of opaque type '{}' not allowed", .{ | 7674 | const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod); |
| 7674 | payload.fmt(sema.mod), | 7675 | return sema.addType(err_union_ty); |
| 7676 | } | ||
| 7677 | |||
| 7678 | fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void { | ||
| 7679 | if (payload_ty.zigTypeTag() == .Opaque) { | ||
| 7680 | return sema.fail(block, payload_src, "error union with payload of opaque type '{}' not allowed", .{ | ||
| 7681 | payload_ty.fmt(sema.mod), | ||
| 7675 | }); | 7682 | }); |
| 7676 | } else if (payload.zigTypeTag() == .ErrorSet) { | 7683 | } else if (payload_ty.zigTypeTag() == .ErrorSet) { |
| 7677 | return sema.fail(block, rhs_src, "error union with payload of error set type '{}' not allowed", .{ | 7684 | return sema.fail(block, payload_src, "error union with payload of error set type '{}' not allowed", .{ |
| 7678 | payload.fmt(sema.mod), | 7685 | payload_ty.fmt(sema.mod), |
| 7679 | }); | 7686 | }); |
| 7680 | } | 7687 | } |
| 7681 | const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod); | ||
| 7682 | return sema.addType(err_union_ty); | ||
| 7683 | } | 7688 | } |
| 7684 | 7689 | ||
| 7685 | fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 7690 | fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -8639,6 +8644,7 @@ fn funcCommon( | ... | @@ -8639,6 +8644,7 @@ fn funcCommon( |
| 8639 | const return_type = if (!inferred_error_set or ret_poison) | 8644 | const return_type = if (!inferred_error_set or ret_poison) |
| 8640 | bare_return_type | 8645 | bare_return_type |
| 8641 | else blk: { | 8646 | else blk: { |
| 8647 | try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src); | ||
| 8642 | const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode); | 8648 | const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode); |
| 8643 | node.data = .{ .func = new_func }; | 8649 | node.data = .{ .func = new_func }; |
| 8644 | maybe_inferred_error_set_node = node; | 8650 | maybe_inferred_error_set_node = node; |
| ... | @@ -8650,15 +8656,15 @@ fn funcCommon( | ... | @@ -8650,15 +8656,15 @@ fn funcCommon( |
| 8650 | }); | 8656 | }); |
| 8651 | }; | 8657 | }; |
| 8652 | 8658 | ||
| 8653 | if (!bare_return_type.isValidReturnType()) { | 8659 | if (!return_type.isValidReturnType()) { |
| 8654 | const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else ""; | 8660 | const opaque_str = if (return_type.zigTypeTag() == .Opaque) "opaque " else ""; |
| 8655 | const msg = msg: { | 8661 | const msg = msg: { |
| 8656 | const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ | 8662 | const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ |
| 8657 | opaque_str, bare_return_type.fmt(sema.mod), | 8663 | opaque_str, return_type.fmt(sema.mod), |
| 8658 | }); | 8664 | }); |
| 8659 | errdefer msg.destroy(sema.gpa); | 8665 | errdefer msg.destroy(sema.gpa); |
| 8660 | 8666 | ||
| 8661 | try sema.addDeclaredHereNote(msg, bare_return_type); | 8667 | try sema.addDeclaredHereNote(msg, return_type); |
| 8662 | break :msg msg; | 8668 | break :msg msg; |
| 8663 | }; | 8669 | }; |
| 8664 | return sema.failWithOwnedErrorMsg(msg); | 8670 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -19540,7 +19546,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -19540,7 +19546,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19540 | const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); | 19546 | const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); |
| 19541 | errdefer msg.destroy(sema.gpa); | 19547 | errdefer msg.destroy(sema.gpa); |
| 19542 | 19548 | ||
| 19543 | try sema.errNote(block, src, msg, "consider using '@qualCast'", .{}); | 19549 | try sema.errNote(block, src, msg, "consider using '@constCast'", .{}); |
| 19544 | break :msg msg; | 19550 | break :msg msg; |
| 19545 | }; | 19551 | }; |
| 19546 | return sema.failWithOwnedErrorMsg(msg); | 19552 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -19550,7 +19556,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -19550,7 +19556,7 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19550 | const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{}); | 19556 | const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{}); |
| 19551 | errdefer msg.destroy(sema.gpa); | 19557 | errdefer msg.destroy(sema.gpa); |
| 19552 | 19558 | ||
| 19553 | try sema.errNote(block, src, msg, "consider using '@qualCast'", .{}); | 19559 | try sema.errNote(block, src, msg, "consider using '@volatileCast'", .{}); |
| 19554 | break :msg msg; | 19560 | break :msg msg; |
| 19555 | }; | 19561 | }; |
| 19556 | return sema.failWithOwnedErrorMsg(msg); | 19562 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -19655,40 +19661,37 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -19655,40 +19661,37 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19655 | return block.addBitCast(aligned_dest_ty, ptr); | 19661 | return block.addBitCast(aligned_dest_ty, ptr); |
| 19656 | } | 19662 | } |
| 19657 | 19663 | ||
| 19658 | fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 19664 | fn zirConstCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 19659 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 19665 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 19660 | const src = inst_data.src(); | 19666 | const src = LazySrcLoc.nodeOffset(extra.node); |
| 19661 | const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 19667 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; |
| 19662 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 19668 | const operand = try sema.resolveInst(extra.operand); |
| 19663 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | ||
| 19664 | const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); | ||
| 19665 | const operand = try sema.resolveInst(extra.rhs); | ||
| 19666 | const operand_ty = sema.typeOf(operand); | 19669 | const operand_ty = sema.typeOf(operand); |
| 19667 | |||
| 19668 | try sema.checkPtrType(block, dest_ty_src, dest_ty); | ||
| 19669 | try sema.checkPtrOperand(block, operand_src, operand_ty); | 19670 | try sema.checkPtrOperand(block, operand_src, operand_ty); |
| 19670 | 19671 | ||
| 19671 | var operand_payload = operand_ty.ptrInfo(); | 19672 | var ptr_info = operand_ty.ptrInfo().data; |
| 19672 | var dest_info = dest_ty.ptrInfo(); | 19673 | ptr_info.mutable = true; |
| 19674 | const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); | ||
| 19673 | 19675 | ||
| 19674 | operand_payload.data.mutable = dest_info.data.mutable; | 19676 | if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { |
| 19675 | operand_payload.data.@"volatile" = dest_info.data.@"volatile"; | 19677 | return sema.addConstant(dest_ty, operand_val); |
| 19678 | } | ||
| 19676 | 19679 | ||
| 19677 | const altered_operand_ty = Type.initPayload(&operand_payload.base); | 19680 | try sema.requireRuntimeBlock(block, src, null); |
| 19678 | if (!altered_operand_ty.eql(dest_ty, sema.mod)) { | 19681 | return block.addBitCast(dest_ty, operand); |
| 19679 | const msg = msg: { | 19682 | } |
| 19680 | const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{}); | ||
| 19681 | errdefer msg.destroy(sema.gpa); | ||
| 19682 | 19683 | ||
| 19683 | dest_info.data.mutable = !operand_ty.isConstPtr(); | 19684 | fn zirVolatileCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 19684 | dest_info.data.@"volatile" = operand_ty.isVolatilePtr(); | 19685 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 19685 | const altered_dest_ty = Type.initPayload(&dest_info.base); | 19686 | const src = LazySrcLoc.nodeOffset(extra.node); |
| 19686 | try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)}); | 19687 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; |
| 19687 | try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)}); | 19688 | const operand = try sema.resolveInst(extra.operand); |
| 19688 | break :msg msg; | 19689 | const operand_ty = sema.typeOf(operand); |
| 19689 | }; | 19690 | try sema.checkPtrOperand(block, operand_src, operand_ty); |
| 19690 | return sema.failWithOwnedErrorMsg(msg); | 19691 | |
| 19691 | } | 19692 | var ptr_info = operand_ty.ptrInfo().data; |
| 19693 | ptr_info.@"volatile" = false; | ||
| 19694 | const dest_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); | ||
| 19692 | 19695 | ||
| 19693 | if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { | 19696 | if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { |
| 19694 | return sema.addConstant(dest_ty, operand_val); | 19697 | return sema.addConstant(dest_ty, operand_val); |
| ... | @@ -21930,7 +21933,7 @@ fn zirCUndef( | ... | @@ -21930,7 +21933,7 @@ fn zirCUndef( |
| 21930 | const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; | 21933 | const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; |
| 21931 | 21934 | ||
| 21932 | const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known"); | 21935 | const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime-known"); |
| 21933 | try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name}); | 21936 | try block.c_import_buf.?.writer().print("#undef {s}\n", .{name}); |
| 21934 | return Air.Inst.Ref.void_value; | 21937 | return Air.Inst.Ref.void_value; |
| 21935 | } | 21938 | } |
| 21936 | 21939 | ||
| ... | @@ -29756,6 +29759,25 @@ fn resolvePeerTypes( | ... | @@ -29756,6 +29759,25 @@ fn resolvePeerTypes( |
| 29756 | continue; | 29759 | continue; |
| 29757 | } | 29760 | } |
| 29758 | }, | 29761 | }, |
| 29762 | .ErrorSet => { | ||
| 29763 | chosen = candidate; | ||
| 29764 | chosen_i = candidate_i + 1; | ||
| 29765 | if (err_set_ty) |chosen_set_ty| { | ||
| 29766 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_set_ty, chosen_ty, src, src)) { | ||
| 29767 | continue; | ||
| 29768 | } | ||
| 29769 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, chosen_ty, chosen_set_ty, src, src)) { | ||
| 29770 | err_set_ty = chosen_ty; | ||
| 29771 | continue; | ||
| 29772 | } | ||
| 29773 | |||
| 29774 | err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, chosen_ty); | ||
| 29775 | continue; | ||
| 29776 | } else { | ||
| 29777 | err_set_ty = chosen_ty; | ||
| 29778 | continue; | ||
| 29779 | } | ||
| 29780 | }, | ||
| 29759 | else => {}, | 29781 | else => {}, |
| 29760 | } | 29782 | } |
| 29761 | 29783 |
src/Zir.zig+6-6| ... | @@ -857,9 +857,6 @@ pub const Inst = struct { | ... | @@ -857,9 +857,6 @@ pub const Inst = struct { |
| 857 | /// Implements the `@ptrCast` builtin. | 857 | /// Implements the `@ptrCast` builtin. |
| 858 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. | 858 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 859 | ptr_cast, | 859 | ptr_cast, |
| 860 | /// Implements the `@qualCast` builtin. | ||
| 861 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. | ||
| 862 | qual_cast, | ||
| 863 | /// Implements the `@truncate` builtin. | 860 | /// Implements the `@truncate` builtin. |
| 864 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. | 861 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 865 | truncate, | 862 | truncate, |
| ... | @@ -1198,7 +1195,6 @@ pub const Inst = struct { | ... | @@ -1198,7 +1195,6 @@ pub const Inst = struct { |
| 1198 | .float_cast, | 1195 | .float_cast, |
| 1199 | .int_cast, | 1196 | .int_cast, |
| 1200 | .ptr_cast, | 1197 | .ptr_cast, |
| 1201 | .qual_cast, | ||
| 1202 | .truncate, | 1198 | .truncate, |
| 1203 | .align_cast, | 1199 | .align_cast, |
| 1204 | .has_field, | 1200 | .has_field, |
| ... | @@ -1488,7 +1484,6 @@ pub const Inst = struct { | ... | @@ -1488,7 +1484,6 @@ pub const Inst = struct { |
| 1488 | .float_cast, | 1484 | .float_cast, |
| 1489 | .int_cast, | 1485 | .int_cast, |
| 1490 | .ptr_cast, | 1486 | .ptr_cast, |
| 1491 | .qual_cast, | ||
| 1492 | .truncate, | 1487 | .truncate, |
| 1493 | .align_cast, | 1488 | .align_cast, |
| 1494 | .has_field, | 1489 | .has_field, |
| ... | @@ -1760,7 +1755,6 @@ pub const Inst = struct { | ... | @@ -1760,7 +1755,6 @@ pub const Inst = struct { |
| 1760 | .float_cast = .pl_node, | 1755 | .float_cast = .pl_node, |
| 1761 | .int_cast = .pl_node, | 1756 | .int_cast = .pl_node, |
| 1762 | .ptr_cast = .pl_node, | 1757 | .ptr_cast = .pl_node, |
| 1763 | .qual_cast = .pl_node, | ||
| 1764 | .truncate = .pl_node, | 1758 | .truncate = .pl_node, |
| 1765 | .align_cast = .pl_node, | 1759 | .align_cast = .pl_node, |
| 1766 | .typeof_builtin = .pl_node, | 1760 | .typeof_builtin = .pl_node, |
| ... | @@ -2004,6 +1998,12 @@ pub const Inst = struct { | ... | @@ -2004,6 +1998,12 @@ pub const Inst = struct { |
| 2004 | /// Implement builtin `@cVaStart`. | 1998 | /// Implement builtin `@cVaStart`. |
| 2005 | /// `operand` is `src_node: i32`. | 1999 | /// `operand` is `src_node: i32`. |
| 2006 | c_va_start, | 2000 | c_va_start, |
| 2001 | /// Implements the `@constCast` builtin. | ||
| 2002 | /// `operand` is payload index to `UnNode`. | ||
| 2003 | const_cast, | ||
| 2004 | /// Implements the `@volatileCast` builtin. | ||
| 2005 | /// `operand` is payload index to `UnNode`. | ||
| 2006 | volatile_cast, | ||
| 2007 | 2007 | ||
| 2008 | pub const InstData = struct { | 2008 | pub const InstData = struct { |
| 2009 | opcode: Extended, | 2009 | opcode: Extended, |
src/codegen/llvm.zig+7-1| ... | @@ -2278,7 +2278,9 @@ pub const Object = struct { | ... | @@ -2278,7 +2278,9 @@ pub const Object = struct { |
| 2278 | 2278 | ||
| 2279 | const full_di_fields: [2]*llvm.DIType = | 2279 | const full_di_fields: [2]*llvm.DIType = |
| 2280 | if (layout.tag_align >= layout.payload_align) | 2280 | if (layout.tag_align >= layout.payload_align) |
| 2281 | .{ tag_di, payload_di } else .{ payload_di, tag_di }; | 2281 | .{ tag_di, payload_di } |
| 2282 | else | ||
| 2283 | .{ payload_di, tag_di }; | ||
| 2282 | 2284 | ||
| 2283 | const full_di_ty = dib.createStructType( | 2285 | const full_di_ty = dib.createStructType( |
| 2284 | compile_unit_scope, | 2286 | compile_unit_scope, |
| ... | @@ -4167,6 +4169,10 @@ pub const DeclGen = struct { | ... | @@ -4167,6 +4169,10 @@ pub const DeclGen = struct { |
| 4167 | if (func.data.owner_decl != decl_index) { | 4169 | if (func.data.owner_decl != decl_index) { |
| 4168 | return self.lowerDeclRefValue(tv, func.data.owner_decl); | 4170 | return self.lowerDeclRefValue(tv, func.data.owner_decl); |
| 4169 | } | 4171 | } |
| 4172 | } else if (decl.val.castTag(.extern_fn)) |func| { | ||
| 4173 | if (func.data.owner_decl != decl_index) { | ||
| 4174 | return self.lowerDeclRefValue(tv, func.data.owner_decl); | ||
| 4175 | } | ||
| 4170 | } | 4176 | } |
| 4171 | 4177 | ||
| 4172 | const is_fn_body = decl.ty.zigTypeTag() == .Fn; | 4178 | const is_fn_body = decl.ty.zigTypeTag() == .Fn; |
src/glibc.zig+1-1| ... | @@ -11,7 +11,7 @@ const target_util = @import("target.zig"); | ... | @@ -11,7 +11,7 @@ const target_util = @import("target.zig"); |
| 11 | const Compilation = @import("Compilation.zig"); | 11 | const Compilation = @import("Compilation.zig"); |
| 12 | const build_options = @import("build_options"); | 12 | const build_options = @import("build_options"); |
| 13 | const trace = @import("tracy.zig").trace; | 13 | const trace = @import("tracy.zig").trace; |
| 14 | const Cache = @import("Cache.zig"); | 14 | const Cache = std.Build.Cache; |
| 15 | const Package = @import("Package.zig"); | 15 | const Package = @import("Package.zig"); |
| 16 | 16 | ||
| 17 | pub const Lib = struct { | 17 | pub const Lib = struct { |
src/link.zig+4-1| ... | @@ -10,7 +10,7 @@ const wasi_libc = @import("wasi_libc.zig"); | ... | @@ -10,7 +10,7 @@ const wasi_libc = @import("wasi_libc.zig"); |
| 10 | 10 | ||
| 11 | const Air = @import("Air.zig"); | 11 | const Air = @import("Air.zig"); |
| 12 | const Allocator = std.mem.Allocator; | 12 | const Allocator = std.mem.Allocator; |
| 13 | const Cache = @import("Cache.zig"); | 13 | const Cache = std.Build.Cache; |
| 14 | const Compilation = @import("Compilation.zig"); | 14 | const Compilation = @import("Compilation.zig"); |
| 15 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 15 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 16 | const Liveness = @import("Liveness.zig"); | 16 | const Liveness = @import("Liveness.zig"); |
| ... | @@ -25,6 +25,8 @@ pub const SystemLib = struct { | ... | @@ -25,6 +25,8 @@ pub const SystemLib = struct { |
| 25 | weak: bool = false, | 25 | weak: bool = false, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | pub const SortSection = enum { name, alignment }; | ||
| 29 | |||
| 28 | pub const CacheMode = enum { incremental, whole }; | 30 | pub const CacheMode = enum { incremental, whole }; |
| 29 | 31 | ||
| 30 | pub fn hashAddSystemLibs( | 32 | pub fn hashAddSystemLibs( |
| ... | @@ -159,6 +161,7 @@ pub const Options = struct { | ... | @@ -159,6 +161,7 @@ pub const Options = struct { |
| 159 | disable_lld_caching: bool, | 161 | disable_lld_caching: bool, |
| 160 | is_test: bool, | 162 | is_test: bool, |
| 161 | hash_style: HashStyle, | 163 | hash_style: HashStyle, |
| 164 | sort_section: ?SortSection, | ||
| 162 | major_subsystem_version: ?u32, | 165 | major_subsystem_version: ?u32, |
| 163 | minor_subsystem_version: ?u32, | 166 | minor_subsystem_version: ?u32, |
| 164 | gc_sections: ?bool = null, | 167 | gc_sections: ?bool = null, |
src/link/Coff/lld.zig+1-1| ... | @@ -5,6 +5,7 @@ const assert = std.debug.assert; | ... | @@ -5,6 +5,7 @@ const assert = std.debug.assert; |
| 5 | const fs = std.fs; | 5 | const fs = std.fs; |
| 6 | const log = std.log.scoped(.link); | 6 | const log = std.log.scoped(.link); |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | const Cache = std.Build.Cache; | ||
| 8 | 9 | ||
| 9 | const mingw = @import("../../mingw.zig"); | 10 | const mingw = @import("../../mingw.zig"); |
| 10 | const link = @import("../../link.zig"); | 11 | const link = @import("../../link.zig"); |
| ... | @@ -13,7 +14,6 @@ const trace = @import("../../tracy.zig").trace; | ... | @@ -13,7 +14,6 @@ const trace = @import("../../tracy.zig").trace; |
| 13 | 14 | ||
| 14 | const Allocator = mem.Allocator; | 15 | const Allocator = mem.Allocator; |
| 15 | 16 | ||
| 16 | const Cache = @import("../../Cache.zig"); | ||
| 17 | const Coff = @import("../Coff.zig"); | 17 | const Coff = @import("../Coff.zig"); |
| 18 | const Compilation = @import("../../Compilation.zig"); | 18 | const Compilation = @import("../../Compilation.zig"); |
| 19 | 19 |
src/link/Elf.zig+7-1| ... | @@ -21,7 +21,7 @@ const trace = @import("../tracy.zig").trace; | ... | @@ -21,7 +21,7 @@ const trace = @import("../tracy.zig").trace; |
| 21 | const Air = @import("../Air.zig"); | 21 | const Air = @import("../Air.zig"); |
| 22 | const Allocator = std.mem.Allocator; | 22 | const Allocator = std.mem.Allocator; |
| 23 | pub const Atom = @import("Elf/Atom.zig"); | 23 | pub const Atom = @import("Elf/Atom.zig"); |
| 24 | const Cache = @import("../Cache.zig"); | 24 | const Cache = std.Build.Cache; |
| 25 | const Compilation = @import("../Compilation.zig"); | 25 | const Compilation = @import("../Compilation.zig"); |
| 26 | const Dwarf = @import("Dwarf.zig"); | 26 | const Dwarf = @import("Dwarf.zig"); |
| 27 | const File = link.File; | 27 | const File = link.File; |
| ... | @@ -1324,6 +1324,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v | ... | @@ -1324,6 +1324,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v |
| 1324 | man.hash.addOptionalBytes(self.base.options.entry); | 1324 | man.hash.addOptionalBytes(self.base.options.entry); |
| 1325 | man.hash.addOptional(self.base.options.image_base_override); | 1325 | man.hash.addOptional(self.base.options.image_base_override); |
| 1326 | man.hash.add(gc_sections); | 1326 | man.hash.add(gc_sections); |
| 1327 | man.hash.addOptional(self.base.options.sort_section); | ||
| 1327 | man.hash.add(self.base.options.eh_frame_hdr); | 1328 | man.hash.add(self.base.options.eh_frame_hdr); |
| 1328 | man.hash.add(self.base.options.emit_relocs); | 1329 | man.hash.add(self.base.options.emit_relocs); |
| 1329 | man.hash.add(self.base.options.rdynamic); | 1330 | man.hash.add(self.base.options.rdynamic); |
| ... | @@ -1488,6 +1489,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v | ... | @@ -1488,6 +1489,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v |
| 1488 | try argv.append(linker_script); | 1489 | try argv.append(linker_script); |
| 1489 | } | 1490 | } |
| 1490 | 1491 | ||
| 1492 | if (self.base.options.sort_section) |how| { | ||
| 1493 | const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)}); | ||
| 1494 | try argv.append(arg); | ||
| 1495 | } | ||
| 1496 | |||
| 1491 | if (gc_sections) { | 1497 | if (gc_sections) { |
| 1492 | try argv.append("--gc-sections"); | 1498 | try argv.append("--gc-sections"); |
| 1493 | } | 1499 | } |
src/link/MachO.zig+1-1| ... | @@ -28,7 +28,7 @@ const Air = @import("../Air.zig"); | ... | @@ -28,7 +28,7 @@ const Air = @import("../Air.zig"); |
| 28 | const Allocator = mem.Allocator; | 28 | const Allocator = mem.Allocator; |
| 29 | const Archive = @import("MachO/Archive.zig"); | 29 | const Archive = @import("MachO/Archive.zig"); |
| 30 | pub const Atom = @import("MachO/Atom.zig"); | 30 | pub const Atom = @import("MachO/Atom.zig"); |
| 31 | const Cache = @import("../Cache.zig"); | 31 | const Cache = std.Build.Cache; |
| 32 | const CodeSignature = @import("MachO/CodeSignature.zig"); | 32 | const CodeSignature = @import("MachO/CodeSignature.zig"); |
| 33 | const Compilation = @import("../Compilation.zig"); | 33 | const Compilation = @import("../Compilation.zig"); |
| 34 | const Dwarf = File.Dwarf; | 34 | const Dwarf = File.Dwarf; |
src/link/MachO/Dylib.zig+2| ... | @@ -166,6 +166,8 @@ pub fn parseFromBinary( | ... | @@ -166,6 +166,8 @@ pub fn parseFromBinary( |
| 166 | const symtab_cmd = cmd.cast(macho.symtab_command).?; | 166 | const symtab_cmd = cmd.cast(macho.symtab_command).?; |
| 167 | const symtab = @ptrCast( | 167 | const symtab = @ptrCast( |
| 168 | [*]const macho.nlist_64, | 168 | [*]const macho.nlist_64, |
| 169 | // Alignment is guaranteed as a dylib is a final linked image and has to have sections | ||
| 170 | // properly aligned in order to be correctly loaded by the loader. | ||
| 169 | @alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]), | 171 | @alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]), |
| 170 | )[0..symtab_cmd.nsyms]; | 172 | )[0..symtab_cmd.nsyms]; |
| 171 | const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize]; | 173 | const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize]; |
src/link/MachO/Object.zig+103-52| ... | @@ -60,14 +60,24 @@ globals_lookup: []i64 = undefined, | ... | @@ -60,14 +60,24 @@ globals_lookup: []i64 = undefined, |
| 60 | /// Can be undefined as set together with in_symtab. | 60 | /// Can be undefined as set together with in_symtab. |
| 61 | relocs_lookup: []RelocEntry = undefined, | 61 | relocs_lookup: []RelocEntry = undefined, |
| 62 | 62 | ||
| 63 | /// All relocations sorted and flatened, sorted by address descending | ||
| 64 | /// per section. | ||
| 65 | relocations: std.ArrayListUnmanaged(macho.relocation_info) = .{}, | ||
| 66 | /// Beginning index to the relocations array for each input section | ||
| 67 | /// defined within this Object file. | ||
| 68 | section_relocs_lookup: std.ArrayListUnmanaged(u32) = .{}, | ||
| 69 | |||
| 70 | /// Data-in-code records sorted by address. | ||
| 71 | data_in_code: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, | ||
| 72 | |||
| 63 | atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, | 73 | atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, |
| 64 | exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, | 74 | exec_atoms: std.ArrayListUnmanaged(AtomIndex) = .{}, |
| 65 | 75 | ||
| 66 | eh_frame_sect: ?macho.section_64 = null, | 76 | eh_frame_sect_id: ?u8 = null, |
| 67 | eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{}, | 77 | eh_frame_relocs_lookup: std.AutoArrayHashMapUnmanaged(u32, Record) = .{}, |
| 68 | eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{}, | 78 | eh_frame_records_lookup: std.AutoArrayHashMapUnmanaged(AtomIndex, u32) = .{}, |
| 69 | 79 | ||
| 70 | unwind_info_sect: ?macho.section_64 = null, | 80 | unwind_info_sect_id: ?u8 = null, |
| 71 | unwind_relocs_lookup: []Record = undefined, | 81 | unwind_relocs_lookup: []Record = undefined, |
| 72 | unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{}, | 82 | unwind_records_lookup: std.AutoHashMapUnmanaged(AtomIndex, u32) = .{}, |
| 73 | 83 | ||
| ... | @@ -100,6 +110,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void { | ... | @@ -100,6 +110,9 @@ pub fn deinit(self: *Object, gpa: Allocator) void { |
| 100 | gpa.free(self.unwind_relocs_lookup); | 110 | gpa.free(self.unwind_relocs_lookup); |
| 101 | } | 111 | } |
| 102 | self.unwind_records_lookup.deinit(gpa); | 112 | self.unwind_records_lookup.deinit(gpa); |
| 113 | self.relocations.deinit(gpa); | ||
| 114 | self.section_relocs_lookup.deinit(gpa); | ||
| 115 | self.data_in_code.deinit(gpa); | ||
| 103 | } | 116 | } |
| 104 | 117 | ||
| 105 | pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void { | 118 | pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void { |
| ... | @@ -137,15 +150,18 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) | ... | @@ -137,15 +150,18 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) |
| 137 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], | 150 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], |
| 138 | }; | 151 | }; |
| 139 | const nsects = self.getSourceSections().len; | 152 | const nsects = self.getSourceSections().len; |
| 153 | |||
| 154 | // Prepopulate relocations per section lookup table. | ||
| 155 | try self.section_relocs_lookup.resize(allocator, nsects); | ||
| 156 | mem.set(u32, self.section_relocs_lookup.items, 0); | ||
| 157 | |||
| 158 | // Parse symtab. | ||
| 140 | const symtab = while (it.next()) |cmd| switch (cmd.cmd()) { | 159 | const symtab = while (it.next()) |cmd| switch (cmd.cmd()) { |
| 141 | .SYMTAB => break cmd.cast(macho.symtab_command).?, | 160 | .SYMTAB => break cmd.cast(macho.symtab_command).?, |
| 142 | else => {}, | 161 | else => {}, |
| 143 | } else return; | 162 | } else return; |
| 144 | 163 | ||
| 145 | self.in_symtab = @ptrCast( | 164 | self.in_symtab = @ptrCast([*]align(1) const macho.nlist_64, self.contents.ptr + symtab.symoff)[0..symtab.nsyms]; |
| 146 | [*]const macho.nlist_64, | ||
| 147 | @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]), | ||
| 148 | )[0..symtab.nsyms]; | ||
| 149 | self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize]; | 165 | self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize]; |
| 150 | 166 | ||
| 151 | self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects); | 167 | self.symtab = try allocator.alloc(macho.nlist_64, self.in_symtab.?.len + nsects); |
| ... | @@ -212,10 +228,10 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) | ... | @@ -212,10 +228,10 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) |
| 212 | } | 228 | } |
| 213 | 229 | ||
| 214 | // Parse __TEXT,__eh_frame header if one exists | 230 | // Parse __TEXT,__eh_frame header if one exists |
| 215 | self.eh_frame_sect = self.getSourceSectionByName("__TEXT", "__eh_frame"); | 231 | self.eh_frame_sect_id = self.getSourceSectionIndexByName("__TEXT", "__eh_frame"); |
| 216 | 232 | ||
| 217 | // Parse __LD,__compact_unwind header if one exists | 233 | // Parse __LD,__compact_unwind header if one exists |
| 218 | self.unwind_info_sect = self.getSourceSectionByName("__LD", "__compact_unwind"); | 234 | self.unwind_info_sect_id = self.getSourceSectionIndexByName("__LD", "__compact_unwind"); |
| 219 | if (self.hasUnwindRecords()) { | 235 | if (self.hasUnwindRecords()) { |
| 220 | self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len); | 236 | self.unwind_relocs_lookup = try allocator.alloc(Record, self.getUnwindRecords().len); |
| 221 | mem.set(Record, self.unwind_relocs_lookup, .{ | 237 | mem.set(Record, self.unwind_relocs_lookup, .{ |
| ... | @@ -354,6 +370,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -354,6 +370,7 @@ pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void { |
| 354 | try self.splitRegularSections(zld, object_id); | 370 | try self.splitRegularSections(zld, object_id); |
| 355 | try self.parseEhFrameSection(zld, object_id); | 371 | try self.parseEhFrameSection(zld, object_id); |
| 356 | try self.parseUnwindInfo(zld, object_id); | 372 | try self.parseUnwindInfo(zld, object_id); |
| 373 | try self.parseDataInCode(zld.gpa); | ||
| 357 | } | 374 | } |
| 358 | 375 | ||
| 359 | /// Splits input regular sections into Atoms. | 376 | /// Splits input regular sections into Atoms. |
| ... | @@ -452,6 +469,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -452,6 +469,8 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void { |
| 452 | zld.sections.items(.header)[out_sect_id].sectName(), | 469 | zld.sections.items(.header)[out_sect_id].sectName(), |
| 453 | }); | 470 | }); |
| 454 | 471 | ||
| 472 | try self.parseRelocs(gpa, section.id); | ||
| 473 | |||
| 455 | const cpu_arch = zld.options.target.cpu.arch; | 474 | const cpu_arch = zld.options.target.cpu.arch; |
| 456 | const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1); | 475 | const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1); |
| 457 | const sect_start_index = sect_sym_index + sect_loc.index; | 476 | const sect_start_index = sect_sym_index + sect_loc.index; |
| ... | @@ -623,25 +642,36 @@ fn filterRelocs( | ... | @@ -623,25 +642,36 @@ fn filterRelocs( |
| 623 | return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) }; | 642 | return .{ .start = @intCast(u32, start), .len = @intCast(u32, len) }; |
| 624 | } | 643 | } |
| 625 | 644 | ||
| 645 | /// Parse all relocs for the input section, and sort in descending order. | ||
| 646 | /// Previously, I have wrongly assumed the compilers output relocations for each | ||
| 647 | /// section in a sorted manner which is simply not true. | ||
| 648 | fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void { | ||
| 649 | const section = self.getSourceSection(sect_id); | ||
| 650 | const start = @intCast(u32, self.relocations.items.len); | ||
| 651 | if (self.getSourceRelocs(section)) |relocs| { | ||
| 652 | try self.relocations.ensureUnusedCapacity(gpa, relocs.len); | ||
| 653 | self.relocations.appendUnalignedSliceAssumeCapacity(relocs); | ||
| 654 | std.sort.sort(macho.relocation_info, self.relocations.items[start..], {}, relocGreaterThan); | ||
| 655 | } | ||
| 656 | self.section_relocs_lookup.items[sect_id] = start; | ||
| 657 | } | ||
| 658 | |||
| 626 | fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { | 659 | fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { |
| 627 | const atom = zld.getAtom(atom_index); | 660 | const atom = zld.getAtom(atom_index); |
| 628 | 661 | ||
| 629 | const source_sect = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { | 662 | const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 630 | const source_sect = self.getSourceSection(source_sym.n_sect - 1); | 663 | break :blk source_sym.n_sect - 1; |
| 631 | assert(!source_sect.isZerofill()); | ||
| 632 | break :blk source_sect; | ||
| 633 | } else blk: { | 664 | } else blk: { |
| 634 | // If there was no matching symbol present in the source symtab, this means | 665 | // If there was no matching symbol present in the source symtab, this means |
| 635 | // we are dealing with either an entire section, or part of it, but also | 666 | // we are dealing with either an entire section, or part of it, but also |
| 636 | // starting at the beginning. | 667 | // starting at the beginning. |
| 637 | const nbase = @intCast(u32, self.in_symtab.?.len); | 668 | const nbase = @intCast(u32, self.in_symtab.?.len); |
| 638 | const sect_id = @intCast(u16, atom.sym_index - nbase); | 669 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 639 | const source_sect = self.getSourceSection(sect_id); | 670 | break :blk sect_id; |
| 640 | assert(!source_sect.isZerofill()); | ||
| 641 | break :blk source_sect; | ||
| 642 | }; | 671 | }; |
| 643 | 672 | const source_sect = self.getSourceSection(source_sect_id); | |
| 644 | const relocs = self.getRelocs(source_sect); | 673 | assert(!source_sect.isZerofill()); |
| 674 | const relocs = self.getRelocs(source_sect_id); | ||
| 645 | 675 | ||
| 646 | self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { | 676 | self.relocs_lookup[atom.sym_index] = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 647 | const offset = source_sym.n_value - source_sect.addr; | 677 | const offset = source_sym.n_value - source_sect.addr; |
| ... | @@ -649,8 +679,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { | ... | @@ -649,8 +679,14 @@ fn cacheRelocs(self: *Object, zld: *Zld, atom_index: AtomIndex) !void { |
| 649 | } else filterRelocs(relocs, 0, atom.size); | 679 | } else filterRelocs(relocs, 0, atom.size); |
| 650 | } | 680 | } |
| 651 | 681 | ||
| 682 | fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation_info) bool { | ||
| 683 | _ = ctx; | ||
| 684 | return lhs.r_address > rhs.r_address; | ||
| 685 | } | ||
| 686 | |||
| 652 | fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | 687 | fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 653 | const sect = self.eh_frame_sect orelse return; | 688 | const sect_id = self.eh_frame_sect_id orelse return; |
| 689 | const sect = self.getSourceSection(sect_id); | ||
| 654 | 690 | ||
| 655 | log.debug("parsing __TEXT,__eh_frame section", .{}); | 691 | log.debug("parsing __TEXT,__eh_frame section", .{}); |
| 656 | 692 | ||
| ... | @@ -660,7 +696,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -660,7 +696,8 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 660 | 696 | ||
| 661 | const gpa = zld.gpa; | 697 | const gpa = zld.gpa; |
| 662 | const cpu_arch = zld.options.target.cpu.arch; | 698 | const cpu_arch = zld.options.target.cpu.arch; |
| 663 | const relocs = self.getRelocs(sect); | 699 | try self.parseRelocs(gpa, sect_id); |
| 700 | const relocs = self.getRelocs(sect_id); | ||
| 664 | 701 | ||
| 665 | var it = self.getEhFrameRecordsIterator(); | 702 | var it = self.getEhFrameRecordsIterator(); |
| 666 | var record_count: u32 = 0; | 703 | var record_count: u32 = 0; |
| ... | @@ -728,12 +765,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -728,12 +765,12 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void { |
| 728 | } | 765 | } |
| 729 | 766 | ||
| 730 | fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { | 767 | fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 731 | const sect = self.unwind_info_sect orelse { | 768 | const sect_id = self.unwind_info_sect_id orelse { |
| 732 | // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`, | 769 | // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`, |
| 733 | // we will try fully synthesising unwind info records to somewhat match Apple ld's | 770 | // we will try fully synthesising unwind info records to somewhat match Apple ld's |
| 734 | // approach. However, we will only synthesise DWARF records and nothing more. For this reason, | 771 | // approach. However, we will only synthesise DWARF records and nothing more. For this reason, |
| 735 | // we still create the output `__TEXT,__unwind_info` section. | 772 | // we still create the output `__TEXT,__unwind_info` section. |
| 736 | if (self.eh_frame_sect != null) { | 773 | if (self.hasEhFrameRecords()) { |
| 737 | if (zld.getSectionByName("__TEXT", "__unwind_info") == null) { | 774 | if (zld.getSectionByName("__TEXT", "__unwind_info") == null) { |
| 738 | _ = try zld.initSection("__TEXT", "__unwind_info", .{}); | 775 | _ = try zld.initSection("__TEXT", "__unwind_info", .{}); |
| 739 | } | 776 | } |
| ... | @@ -758,15 +795,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { | ... | @@ -758,15 +795,15 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void { |
| 758 | if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true; | 795 | if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) break true; |
| 759 | } else false; | 796 | } else false; |
| 760 | 797 | ||
| 761 | if (needs_eh_frame) { | 798 | if (needs_eh_frame and !self.hasEhFrameRecords()) { |
| 762 | if (self.eh_frame_sect == null) { | 799 | log.err("missing __TEXT,__eh_frame section", .{}); |
| 763 | log.err("missing __TEXT,__eh_frame section", .{}); | 800 | log.err(" in object {s}", .{self.name}); |
| 764 | log.err(" in object {s}", .{self.name}); | 801 | return error.MissingSection; |
| 765 | return error.MissingSection; | ||
| 766 | } | ||
| 767 | } | 802 | } |
| 768 | 803 | ||
| 769 | const relocs = self.getRelocs(sect); | 804 | try self.parseRelocs(gpa, sect_id); |
| 805 | const relocs = self.getRelocs(sect_id); | ||
| 806 | |||
| 770 | for (unwind_records) |record, record_id| { | 807 | for (unwind_records) |record, record_id| { |
| 771 | const offset = record_id * @sizeOf(macho.compact_unwind_entry); | 808 | const offset = record_id * @sizeOf(macho.compact_unwind_entry); |
| 772 | const rel_pos = filterRelocs( | 809 | const rel_pos = filterRelocs( |
| ... | @@ -806,25 +843,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 { | ... | @@ -806,25 +843,23 @@ pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 { |
| 806 | return symtab[mapped_index]; | 843 | return symtab[mapped_index]; |
| 807 | } | 844 | } |
| 808 | 845 | ||
| 809 | pub fn getSourceSection(self: Object, index: u16) macho.section_64 { | 846 | pub fn getSourceSection(self: Object, index: u8) macho.section_64 { |
| 810 | const sections = self.getSourceSections(); | 847 | const sections = self.getSourceSections(); |
| 811 | assert(index < sections.len); | 848 | assert(index < sections.len); |
| 812 | return sections[index]; | 849 | return sections[index]; |
| 813 | } | 850 | } |
| 814 | 851 | ||
| 815 | pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 { | 852 | pub fn getSourceSectionByName(self: Object, segname: []const u8, sectname: []const u8) ?macho.section_64 { |
| 853 | const index = self.getSourceSectionIndexByName(segname, sectname) orelse return null; | ||
| 816 | const sections = self.getSourceSections(); | 854 | const sections = self.getSourceSections(); |
| 817 | for (sections) |sect| { | 855 | return sections[index]; |
| 818 | if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName())) | ||
| 819 | return sect; | ||
| 820 | } else return null; | ||
| 821 | } | 856 | } |
| 822 | 857 | ||
| 823 | pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 { | 858 | pub fn getSourceSectionIndexByName(self: Object, segname: []const u8, sectname: []const u8) ?u8 { |
| 824 | const sections = self.getSourceSections(); | 859 | const sections = self.getSourceSections(); |
| 825 | for (sections) |sect, i| { | 860 | for (sections) |sect, i| { |
| 826 | if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName())) | 861 | if (mem.eql(u8, segname, sect.segName()) and mem.eql(u8, sectname, sect.sectName())) |
| 827 | return @intCast(u8, i + 1); | 862 | return @intCast(u8, i); |
| 828 | } else return null; | 863 | } else return null; |
| 829 | } | 864 | } |
| 830 | 865 | ||
| ... | @@ -841,24 +876,27 @@ pub fn getSourceSections(self: Object) []const macho.section_64 { | ... | @@ -841,24 +876,27 @@ pub fn getSourceSections(self: Object) []const macho.section_64 { |
| 841 | } else unreachable; | 876 | } else unreachable; |
| 842 | } | 877 | } |
| 843 | 878 | ||
| 844 | pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry { | 879 | pub fn parseDataInCode(self: *Object, gpa: Allocator) !void { |
| 845 | var it = LoadCommandIterator{ | 880 | var it = LoadCommandIterator{ |
| 846 | .ncmds = self.header.ncmds, | 881 | .ncmds = self.header.ncmds, |
| 847 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], | 882 | .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds], |
| 848 | }; | 883 | }; |
| 849 | while (it.next()) |cmd| { | 884 | const cmd = while (it.next()) |cmd| { |
| 850 | switch (cmd.cmd()) { | 885 | switch (cmd.cmd()) { |
| 851 | .DATA_IN_CODE => { | 886 | .DATA_IN_CODE => break cmd.cast(macho.linkedit_data_command).?, |
| 852 | const dice = cmd.cast(macho.linkedit_data_command).?; | ||
| 853 | const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry)); | ||
| 854 | return @ptrCast( | ||
| 855 | [*]const macho.data_in_code_entry, | ||
| 856 | @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]), | ||
| 857 | )[0..ndice]; | ||
| 858 | }, | ||
| 859 | else => {}, | 887 | else => {}, |
| 860 | } | 888 | } |
| 861 | } else return null; | 889 | } else return; |
| 890 | const ndice = @divExact(cmd.datasize, @sizeOf(macho.data_in_code_entry)); | ||
| 891 | const dice = @ptrCast([*]align(1) const macho.data_in_code_entry, self.contents.ptr + cmd.dataoff)[0..ndice]; | ||
| 892 | try self.data_in_code.ensureTotalCapacityPrecise(gpa, dice.len); | ||
| 893 | self.data_in_code.appendUnalignedSliceAssumeCapacity(dice); | ||
| 894 | std.sort.sort(macho.data_in_code_entry, self.data_in_code.items, {}, diceLessThan); | ||
| 895 | } | ||
| 896 | |||
| 897 | fn diceLessThan(ctx: void, lhs: macho.data_in_code_entry, rhs: macho.data_in_code_entry) bool { | ||
| 898 | _ = ctx; | ||
| 899 | return lhs.offset < rhs.offset; | ||
| 862 | } | 900 | } |
| 863 | 901 | ||
| 864 | fn parseDysymtab(self: Object) ?macho.dysymtab_command { | 902 | fn parseDysymtab(self: Object) ?macho.dysymtab_command { |
| ... | @@ -914,11 +952,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 { | ... | @@ -914,11 +952,18 @@ pub fn getSectionAliasSymbolPtr(self: *Object, sect_id: u8) *macho.nlist_64 { |
| 914 | return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)]; | 952 | return &self.symtab[self.getSectionAliasSymbolIndex(sect_id)]; |
| 915 | } | 953 | } |
| 916 | 954 | ||
| 917 | pub fn getRelocs(self: Object, sect: macho.section_64) []align(1) const macho.relocation_info { | 955 | fn getSourceRelocs(self: Object, sect: macho.section_64) ?[]align(1) const macho.relocation_info { |
| 918 | if (sect.nreloc == 0) return &[0]macho.relocation_info{}; | 956 | if (sect.nreloc == 0) return null; |
| 919 | return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc]; | 957 | return @ptrCast([*]align(1) const macho.relocation_info, self.contents.ptr + sect.reloff)[0..sect.nreloc]; |
| 920 | } | 958 | } |
| 921 | 959 | ||
| 960 | pub fn getRelocs(self: Object, sect_id: u8) []const macho.relocation_info { | ||
| 961 | const sect = self.getSourceSection(sect_id); | ||
| 962 | const start = self.section_relocs_lookup.items[sect_id]; | ||
| 963 | const len = sect.nreloc; | ||
| 964 | return self.relocations.items[start..][0..len]; | ||
| 965 | } | ||
| 966 | |||
| 922 | pub fn getSymbolName(self: Object, index: u32) []const u8 { | 967 | pub fn getSymbolName(self: Object, index: u32) []const u8 { |
| 923 | const strtab = self.in_strtab.?; | 968 | const strtab = self.in_strtab.?; |
| 924 | const sym = self.symtab[index]; | 969 | const sym = self.symtab[index]; |
| ... | @@ -976,22 +1021,28 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex { | ... | @@ -976,22 +1021,28 @@ pub fn getAtomIndexForSymbol(self: Object, sym_index: u32) ?AtomIndex { |
| 976 | } | 1021 | } |
| 977 | 1022 | ||
| 978 | pub fn hasUnwindRecords(self: Object) bool { | 1023 | pub fn hasUnwindRecords(self: Object) bool { |
| 979 | return self.unwind_info_sect != null; | 1024 | return self.unwind_info_sect_id != null; |
| 980 | } | 1025 | } |
| 981 | 1026 | ||
| 982 | pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry { | 1027 | pub fn getUnwindRecords(self: Object) []align(1) const macho.compact_unwind_entry { |
| 983 | const sect = self.unwind_info_sect orelse return &[0]macho.compact_unwind_entry{}; | 1028 | const sect_id = self.unwind_info_sect_id orelse return &[0]macho.compact_unwind_entry{}; |
| 1029 | const sect = self.getSourceSection(sect_id); | ||
| 984 | const data = self.getSectionContents(sect); | 1030 | const data = self.getSectionContents(sect); |
| 985 | const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); | 1031 | const num_entries = @divExact(data.len, @sizeOf(macho.compact_unwind_entry)); |
| 986 | return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries]; | 1032 | return @ptrCast([*]align(1) const macho.compact_unwind_entry, data)[0..num_entries]; |
| 987 | } | 1033 | } |
| 988 | 1034 | ||
| 989 | pub fn hasEhFrameRecords(self: Object) bool { | 1035 | pub fn hasEhFrameRecords(self: Object) bool { |
| 990 | return self.eh_frame_sect != null; | 1036 | return self.eh_frame_sect_id != null; |
| 991 | } | 1037 | } |
| 992 | 1038 | ||
| 993 | pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator { | 1039 | pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator { |
| 994 | const sect = self.eh_frame_sect orelse return .{ .data = &[0]u8{} }; | 1040 | const sect_id = self.eh_frame_sect_id orelse return .{ .data = &[0]u8{} }; |
| 1041 | const sect = self.getSourceSection(sect_id); | ||
| 995 | const data = self.getSectionContents(sect); | 1042 | const data = self.getSectionContents(sect); |
| 996 | return .{ .data = data }; | 1043 | return .{ .data = data }; |
| 997 | } | 1044 | } |
| 1045 | |||
| 1046 | pub fn hasDataInCode(self: Object) bool { | ||
| 1047 | return self.data_in_code.items.len > 0; | ||
| 1048 | } |
src/link/MachO/UnwindInfo.zig+5-9| ... | @@ -68,7 +68,7 @@ const Page = struct { | ... | @@ -68,7 +68,7 @@ const Page = struct { |
| 68 | start: RecordIndex, | 68 | start: RecordIndex, |
| 69 | count: u16, | 69 | count: u16, |
| 70 | page_encodings: [max_compact_encodings]RecordIndex = undefined, | 70 | page_encodings: [max_compact_encodings]RecordIndex = undefined, |
| 71 | page_encodings_count: u8 = 0, | 71 | page_encodings_count: u9 = 0, |
| 72 | 72 | ||
| 73 | fn appendPageEncoding(page: *Page, record_id: RecordIndex) void { | 73 | fn appendPageEncoding(page: *Page, record_id: RecordIndex) void { |
| 74 | assert(page.page_encodings_count <= max_compact_encodings); | 74 | assert(page.page_encodings_count <= max_compact_encodings); |
| ... | @@ -81,13 +81,13 @@ const Page = struct { | ... | @@ -81,13 +81,13 @@ const Page = struct { |
| 81 | info: *const UnwindInfo, | 81 | info: *const UnwindInfo, |
| 82 | enc: macho.compact_unwind_encoding_t, | 82 | enc: macho.compact_unwind_encoding_t, |
| 83 | ) ?u8 { | 83 | ) ?u8 { |
| 84 | comptime var index: u8 = 0; | 84 | comptime var index: u9 = 0; |
| 85 | inline while (index < max_compact_encodings) : (index += 1) { | 85 | inline while (index < max_compact_encodings) : (index += 1) { |
| 86 | if (index >= page.page_encodings_count) return null; | 86 | if (index >= page.page_encodings_count) return null; |
| 87 | const record_id = page.page_encodings[index]; | 87 | const record_id = page.page_encodings[index]; |
| 88 | const record = info.records.items[record_id]; | 88 | const record = info.records.items[record_id]; |
| 89 | if (record.compactUnwindEncoding == enc) { | 89 | if (record.compactUnwindEncoding == enc) { |
| 90 | return index; | 90 | return @intCast(u8, index); |
| 91 | } | 91 | } |
| 92 | } | 92 | } |
| 93 | return null; | 93 | return null; |
| ... | @@ -703,15 +703,11 @@ pub fn parseRelocTarget( | ... | @@ -703,15 +703,11 @@ pub fn parseRelocTarget( |
| 703 | } else return sym_loc; | 703 | } else return sym_loc; |
| 704 | } | 704 | } |
| 705 | 705 | ||
| 706 | fn getRelocs( | 706 | fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info { |
| 707 | zld: *Zld, | ||
| 708 | object_id: u32, | ||
| 709 | record_id: usize, | ||
| 710 | ) []align(1) const macho.relocation_info { | ||
| 711 | const object = &zld.objects.items[object_id]; | 707 | const object = &zld.objects.items[object_id]; |
| 712 | assert(object.hasUnwindRecords()); | 708 | assert(object.hasUnwindRecords()); |
| 713 | const rel_pos = object.unwind_relocs_lookup[record_id].reloc; | 709 | const rel_pos = object.unwind_relocs_lookup[record_id].reloc; |
| 714 | const relocs = object.getRelocs(object.unwind_info_sect.?); | 710 | const relocs = object.getRelocs(object.unwind_info_sect_id.?); |
| 715 | return relocs[rel_pos.start..][0..rel_pos.len]; | 711 | return relocs[rel_pos.start..][0..rel_pos.len]; |
| 716 | } | 712 | } |
| 717 | 713 |
src/link/MachO/ZldAtom.zig+14-17| ... | @@ -143,7 +143,7 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u | ... | @@ -143,7 +143,7 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: AtomIndex, sym_index: u32) u |
| 143 | sym.n_value | 143 | sym.n_value |
| 144 | else blk: { | 144 | else blk: { |
| 145 | const nbase = @intCast(u32, object.in_symtab.?.len); | 145 | const nbase = @intCast(u32, object.in_symtab.?.len); |
| 146 | const sect_id = @intCast(u16, atom.sym_index - nbase); | 146 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 147 | const source_sect = object.getSourceSection(sect_id); | 147 | const source_sect = object.getSourceSection(sect_id); |
| 148 | break :blk source_sect.addr; | 148 | break :blk source_sect.addr; |
| 149 | }; | 149 | }; |
| ... | @@ -180,7 +180,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext { | ... | @@ -180,7 +180,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: AtomIndex) RelocContext { |
| 180 | }; | 180 | }; |
| 181 | } | 181 | } |
| 182 | const nbase = @intCast(u32, object.in_symtab.?.len); | 182 | const nbase = @intCast(u32, object.in_symtab.?.len); |
| 183 | const sect_id = @intCast(u16, atom.sym_index - nbase); | 183 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 184 | const source_sect = object.getSourceSection(sect_id); | 184 | const source_sect = object.getSourceSection(sect_id); |
| 185 | return .{ | 185 | return .{ |
| 186 | .base_addr = source_sect.addr, | 186 | .base_addr = source_sect.addr, |
| ... | @@ -724,7 +724,7 @@ fn resolveRelocsArm64( | ... | @@ -724,7 +724,7 @@ fn resolveRelocsArm64( |
| 724 | 724 | ||
| 725 | if (rel.r_extern == 0) { | 725 | if (rel.r_extern == 0) { |
| 726 | const base_addr = if (target.sym_index > object.source_address_lookup.len) | 726 | const base_addr = if (target.sym_index > object.source_address_lookup.len) |
| 727 | @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr) | 727 | @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr) |
| 728 | else | 728 | else |
| 729 | object.source_address_lookup[target.sym_index]; | 729 | object.source_address_lookup[target.sym_index]; |
| 730 | ptr_addend -= base_addr; | 730 | ptr_addend -= base_addr; |
| ... | @@ -861,7 +861,7 @@ fn resolveRelocsX86( | ... | @@ -861,7 +861,7 @@ fn resolveRelocsX86( |
| 861 | 861 | ||
| 862 | if (rel.r_extern == 0) { | 862 | if (rel.r_extern == 0) { |
| 863 | const base_addr = if (target.sym_index > object.source_address_lookup.len) | 863 | const base_addr = if (target.sym_index > object.source_address_lookup.len) |
| 864 | @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr) | 864 | @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr) |
| 865 | else | 865 | else |
| 866 | object.source_address_lookup[target.sym_index]; | 866 | object.source_address_lookup[target.sym_index]; |
| 867 | addend += @intCast(i32, @intCast(i64, context.base_addr) + rel.r_address + 4 - | 867 | addend += @intCast(i32, @intCast(i64, context.base_addr) + rel.r_address + 4 - |
| ... | @@ -884,7 +884,7 @@ fn resolveRelocsX86( | ... | @@ -884,7 +884,7 @@ fn resolveRelocsX86( |
| 884 | 884 | ||
| 885 | if (rel.r_extern == 0) { | 885 | if (rel.r_extern == 0) { |
| 886 | const base_addr = if (target.sym_index > object.source_address_lookup.len) | 886 | const base_addr = if (target.sym_index > object.source_address_lookup.len) |
| 887 | @intCast(i64, object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr) | 887 | @intCast(i64, object.getSourceSection(@intCast(u8, rel.r_symbolnum - 1)).addr) |
| 888 | else | 888 | else |
| 889 | object.source_address_lookup[target.sym_index]; | 889 | object.source_address_lookup[target.sym_index]; |
| 890 | addend -= base_addr; | 890 | addend -= base_addr; |
| ... | @@ -928,7 +928,7 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 { | ... | @@ -928,7 +928,7 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 { |
| 928 | // we are dealing with either an entire section, or part of it, but also | 928 | // we are dealing with either an entire section, or part of it, but also |
| 929 | // starting at the beginning. | 929 | // starting at the beginning. |
| 930 | const nbase = @intCast(u32, object.in_symtab.?.len); | 930 | const nbase = @intCast(u32, object.in_symtab.?.len); |
| 931 | const sect_id = @intCast(u16, atom.sym_index - nbase); | 931 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 932 | const source_sect = object.getSourceSection(sect_id); | 932 | const source_sect = object.getSourceSection(sect_id); |
| 933 | assert(!source_sect.isZerofill()); | 933 | assert(!source_sect.isZerofill()); |
| 934 | const code = object.getSectionContents(source_sect); | 934 | const code = object.getSectionContents(source_sect); |
| ... | @@ -943,28 +943,25 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 { | ... | @@ -943,28 +943,25 @@ pub fn getAtomCode(zld: *Zld, atom_index: AtomIndex) []const u8 { |
| 943 | return code[offset..][0..code_len]; | 943 | return code[offset..][0..code_len]; |
| 944 | } | 944 | } |
| 945 | 945 | ||
| 946 | pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []align(1) const macho.relocation_info { | 946 | pub fn getAtomRelocs(zld: *Zld, atom_index: AtomIndex) []const macho.relocation_info { |
| 947 | const atom = zld.getAtom(atom_index); | 947 | const atom = zld.getAtom(atom_index); |
| 948 | assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs. | 948 | assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs. |
| 949 | const object = zld.objects.items[atom.getFile().?]; | 949 | const object = zld.objects.items[atom.getFile().?]; |
| 950 | const cache = object.relocs_lookup[atom.sym_index]; | 950 | const cache = object.relocs_lookup[atom.sym_index]; |
| 951 | 951 | ||
| 952 | const source_sect = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: { | 952 | const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: { |
| 953 | const source_sect = object.getSourceSection(source_sym.n_sect - 1); | 953 | break :blk source_sym.n_sect - 1; |
| 954 | assert(!source_sect.isZerofill()); | ||
| 955 | break :blk source_sect; | ||
| 956 | } else blk: { | 954 | } else blk: { |
| 957 | // If there was no matching symbol present in the source symtab, this means | 955 | // If there was no matching symbol present in the source symtab, this means |
| 958 | // we are dealing with either an entire section, or part of it, but also | 956 | // we are dealing with either an entire section, or part of it, but also |
| 959 | // starting at the beginning. | 957 | // starting at the beginning. |
| 960 | const nbase = @intCast(u32, object.in_symtab.?.len); | 958 | const nbase = @intCast(u32, object.in_symtab.?.len); |
| 961 | const sect_id = @intCast(u16, atom.sym_index - nbase); | 959 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 962 | const source_sect = object.getSourceSection(sect_id); | 960 | break :blk sect_id; |
| 963 | assert(!source_sect.isZerofill()); | ||
| 964 | break :blk source_sect; | ||
| 965 | }; | 961 | }; |
| 966 | 962 | const source_sect = object.getSourceSection(source_sect_id); | |
| 967 | const relocs = object.getRelocs(source_sect); | 963 | assert(!source_sect.isZerofill()); |
| 964 | const relocs = object.getRelocs(source_sect_id); | ||
| 968 | return relocs[cache.start..][0..cache.len]; | 965 | return relocs[cache.start..][0..cache.len]; |
| 969 | } | 966 | } |
| 970 | 967 |
src/link/MachO/dead_strip.zig+4-3| ... | @@ -88,7 +88,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { | ... | @@ -88,7 +88,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void { |
| 88 | source_sym.n_sect - 1 | 88 | source_sym.n_sect - 1 |
| 89 | else sect_id: { | 89 | else sect_id: { |
| 90 | const nbase = @intCast(u32, object.in_symtab.?.len); | 90 | const nbase = @intCast(u32, object.in_symtab.?.len); |
| 91 | const sect_id = @intCast(u16, atom.sym_index - nbase); | 91 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 92 | break :sect_id sect_id; | 92 | break :sect_id sect_id; |
| 93 | }; | 93 | }; |
| 94 | const source_sect = object.getSourceSection(sect_id); | 94 | const source_sect = object.getSourceSection(sect_id); |
| ... | @@ -223,7 +223,7 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void { | ... | @@ -223,7 +223,7 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void { |
| 223 | source_sym.n_sect - 1 | 223 | source_sym.n_sect - 1 |
| 224 | else blk: { | 224 | else blk: { |
| 225 | const nbase = @intCast(u32, object.in_symtab.?.len); | 225 | const nbase = @intCast(u32, object.in_symtab.?.len); |
| 226 | const sect_id = @intCast(u16, atom.sym_index - nbase); | 226 | const sect_id = @intCast(u8, atom.sym_index - nbase); |
| 227 | break :blk sect_id; | 227 | break :blk sect_id; |
| 228 | }; | 228 | }; |
| 229 | const source_sect = object.getSourceSection(sect_id); | 229 | const source_sect = object.getSourceSection(sect_id); |
| ... | @@ -350,8 +350,9 @@ fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *A | ... | @@ -350,8 +350,9 @@ fn markEhFrameRecord(zld: *Zld, object_id: u32, atom_index: AtomIndex, alive: *A |
| 350 | } | 350 | } |
| 351 | }, | 351 | }, |
| 352 | .x86_64 => { | 352 | .x86_64 => { |
| 353 | const sect = object.getSourceSection(object.eh_frame_sect_id.?); | ||
| 353 | const lsda_ptr = try fde.getLsdaPointer(cie, .{ | 354 | const lsda_ptr = try fde.getLsdaPointer(cie, .{ |
| 354 | .base_addr = object.eh_frame_sect.?.addr, | 355 | .base_addr = sect.addr, |
| 355 | .base_offset = fde_offset, | 356 | .base_offset = fde_offset, |
| 356 | }); | 357 | }); |
| 357 | if (lsda_ptr) |lsda_address| { | 358 | if (lsda_ptr) |lsda_address| { |
src/link/MachO/eh_frame.zig+4-7| ... | @@ -171,8 +171,9 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { | ... | @@ -171,8 +171,9 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void { |
| 171 | const cie_record = eh_records.get( | 171 | const cie_record = eh_records.get( |
| 172 | eh_frame_offset + 4 - fde_record.getCiePointer(), | 172 | eh_frame_offset + 4 - fde_record.getCiePointer(), |
| 173 | ).?; | 173 | ).?; |
| 174 | const eh_frame_sect = object.getSourceSection(object.eh_frame_sect_id.?); | ||
| 174 | const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{ | 175 | const source_lsda_ptr = try fde_record.getLsdaPointer(cie_record, .{ |
| 175 | .base_addr = object.eh_frame_sect.?.addr, | 176 | .base_addr = eh_frame_sect.addr, |
| 176 | .base_offset = fde_record_offset, | 177 | .base_offset = fde_record_offset, |
| 177 | }); | 178 | }); |
| 178 | if (source_lsda_ptr) |ptr| { | 179 | if (source_lsda_ptr) |ptr| { |
| ... | @@ -552,16 +553,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { | ... | @@ -552,16 +553,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { |
| 552 | }; | 553 | }; |
| 553 | } | 554 | } |
| 554 | 555 | ||
| 555 | pub fn getRelocs( | 556 | pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info { |
| 556 | zld: *Zld, | ||
| 557 | object_id: u32, | ||
| 558 | source_offset: u32, | ||
| 559 | ) []align(1) const macho.relocation_info { | ||
| 560 | const object = &zld.objects.items[object_id]; | 557 | const object = &zld.objects.items[object_id]; |
| 561 | assert(object.hasEhFrameRecords()); | 558 | assert(object.hasEhFrameRecords()); |
| 562 | const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse | 559 | const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse |
| 563 | return &[0]macho.relocation_info{}; | 560 | return &[0]macho.relocation_info{}; |
| 564 | const all_relocs = object.getRelocs(object.eh_frame_sect.?); | 561 | const all_relocs = object.getRelocs(object.eh_frame_sect_id.?); |
| 565 | return all_relocs[urel.reloc.start..][0..urel.reloc.len]; | 562 | return all_relocs[urel.reloc.start..][0..urel.reloc.len]; |
| 566 | } | 563 | } |
| 567 | 564 |
src/link/MachO/zld.zig+16-12| ... | @@ -20,7 +20,7 @@ const trace = @import("../../tracy.zig").trace; | ... | @@ -20,7 +20,7 @@ const trace = @import("../../tracy.zig").trace; |
| 20 | const Allocator = mem.Allocator; | 20 | const Allocator = mem.Allocator; |
| 21 | const Archive = @import("Archive.zig"); | 21 | const Archive = @import("Archive.zig"); |
| 22 | const Atom = @import("ZldAtom.zig"); | 22 | const Atom = @import("ZldAtom.zig"); |
| 23 | const Cache = @import("../../Cache.zig"); | 23 | const Cache = std.Build.Cache; |
| 24 | const CodeSignature = @import("CodeSignature.zig"); | 24 | const CodeSignature = @import("CodeSignature.zig"); |
| 25 | const Compilation = @import("../../Compilation.zig"); | 25 | const Compilation = @import("../../Compilation.zig"); |
| 26 | const DwarfInfo = @import("DwarfInfo.zig"); | 26 | const DwarfInfo = @import("DwarfInfo.zig"); |
| ... | @@ -1065,7 +1065,13 @@ pub const Zld = struct { | ... | @@ -1065,7 +1065,13 @@ pub const Zld = struct { |
| 1065 | assert(offsets.items.len > 0); | 1065 | assert(offsets.items.len > 0); |
| 1066 | 1066 | ||
| 1067 | const object_id = @intCast(u16, self.objects.items.len); | 1067 | const object_id = @intCast(u16, self.objects.items.len); |
| 1068 | const object = try archive.parseObject(gpa, cpu_arch, offsets.items[0]); | 1068 | const object = archive.parseObject(gpa, cpu_arch, offsets.items[0]) catch |e| switch (e) { |
| 1069 | error.MismatchedCpuArchitecture => { | ||
| 1070 | log.err("CPU architecture mismatch found in {s}", .{archive.name}); | ||
| 1071 | return e; | ||
| 1072 | }, | ||
| 1073 | else => return e, | ||
| 1074 | }; | ||
| 1069 | try self.objects.append(gpa, object); | 1075 | try self.objects.append(gpa, object); |
| 1070 | try self.resolveSymbolsInObject(object_id, resolver); | 1076 | try self.resolveSymbolsInObject(object_id, resolver); |
| 1071 | 1077 | ||
| ... | @@ -2391,22 +2397,20 @@ pub const Zld = struct { | ... | @@ -2391,22 +2397,20 @@ pub const Zld = struct { |
| 2391 | const text_sect_header = self.sections.items(.header)[text_sect_id]; | 2397 | const text_sect_header = self.sections.items(.header)[text_sect_id]; |
| 2392 | 2398 | ||
| 2393 | for (self.objects.items) |object| { | 2399 | for (self.objects.items) |object| { |
| 2394 | const dice = object.parseDataInCode() orelse continue; | 2400 | if (!object.hasDataInCode()) continue; |
| 2401 | const dice = object.data_in_code.items; | ||
| 2395 | try out_dice.ensureUnusedCapacity(dice.len); | 2402 | try out_dice.ensureUnusedCapacity(dice.len); |
| 2396 | 2403 | ||
| 2397 | for (object.atoms.items) |atom_index| { | 2404 | for (object.exec_atoms.items) |atom_index| { |
| 2398 | const atom = self.getAtom(atom_index); | 2405 | const atom = self.getAtom(atom_index); |
| 2399 | const sym = self.getSymbol(atom.getSymbolWithLoc()); | 2406 | const sym = self.getSymbol(atom.getSymbolWithLoc()); |
| 2400 | const sect_id = sym.n_sect - 1; | 2407 | if (sym.n_desc == N_DEAD) continue; |
| 2401 | if (sect_id != text_sect_id) { | ||
| 2402 | continue; | ||
| 2403 | } | ||
| 2404 | 2408 | ||
| 2405 | const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym| | 2409 | const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym| |
| 2406 | source_sym.n_value | 2410 | source_sym.n_value |
| 2407 | else blk: { | 2411 | else blk: { |
| 2408 | const nbase = @intCast(u32, object.in_symtab.?.len); | 2412 | const nbase = @intCast(u32, object.in_symtab.?.len); |
| 2409 | const source_sect_id = @intCast(u16, atom.sym_index - nbase); | 2413 | const source_sect_id = @intCast(u8, atom.sym_index - nbase); |
| 2410 | break :blk object.getSourceSection(source_sect_id).addr; | 2414 | break :blk object.getSourceSection(source_sect_id).addr; |
| 2411 | }; | 2415 | }; |
| 2412 | const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); | 2416 | const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size); |
| ... | @@ -2699,12 +2703,12 @@ pub const Zld = struct { | ... | @@ -2699,12 +2703,12 @@ pub const Zld = struct { |
| 2699 | // Exclude region comprising all symbol stabs. | 2703 | // Exclude region comprising all symbol stabs. |
| 2700 | const nlocals = self.dysymtab_cmd.nlocalsym; | 2704 | const nlocals = self.dysymtab_cmd.nlocalsym; |
| 2701 | 2705 | ||
| 2702 | const locals_buf = try self.gpa.alloc(u8, nlocals * @sizeOf(macho.nlist_64)); | 2706 | const locals = try self.gpa.alloc(macho.nlist_64, nlocals); |
| 2703 | defer self.gpa.free(locals_buf); | 2707 | defer self.gpa.free(locals); |
| 2704 | 2708 | ||
| 2709 | const locals_buf = @ptrCast([*]u8, locals.ptr)[0 .. @sizeOf(macho.nlist_64) * nlocals]; | ||
| 2705 | const amt = try self.file.preadAll(locals_buf, self.symtab_cmd.symoff); | 2710 | const amt = try self.file.preadAll(locals_buf, self.symtab_cmd.symoff); |
| 2706 | if (amt != locals_buf.len) return error.InputOutput; | 2711 | if (amt != locals_buf.len) return error.InputOutput; |
| 2707 | const locals = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), locals_buf))[0..nlocals]; | ||
| 2708 | 2712 | ||
| 2709 | const istab: usize = for (locals) |local, i| { | 2713 | const istab: usize = for (locals) |local, i| { |
| 2710 | if (local.stab()) break i; | 2714 | if (local.stab()) break i; |
src/link/Wasm.zig+1-1| ... | @@ -20,7 +20,7 @@ const lldMain = @import("../main.zig").lldMain; | ... | @@ -20,7 +20,7 @@ const lldMain = @import("../main.zig").lldMain; |
| 20 | const trace = @import("../tracy.zig").trace; | 20 | const trace = @import("../tracy.zig").trace; |
| 21 | const build_options = @import("build_options"); | 21 | const build_options = @import("build_options"); |
| 22 | const wasi_libc = @import("../wasi_libc.zig"); | 22 | const wasi_libc = @import("../wasi_libc.zig"); |
| 23 | const Cache = @import("../Cache.zig"); | 23 | const Cache = std.Build.Cache; |
| 24 | const Type = @import("../type.zig").Type; | 24 | const Type = @import("../type.zig").Type; |
| 25 | const TypedValue = @import("../TypedValue.zig"); | 25 | const TypedValue = @import("../TypedValue.zig"); |
| 26 | const LlvmObject = @import("../codegen/llvm.zig").Object; | 26 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
src/main.zig+48-6| ... | @@ -20,7 +20,7 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | ... | @@ -20,7 +20,7 @@ const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 20 | const wasi_libc = @import("wasi_libc.zig"); | 20 | const wasi_libc = @import("wasi_libc.zig"); |
| 21 | const translate_c = @import("translate_c.zig"); | 21 | const translate_c = @import("translate_c.zig"); |
| 22 | const clang = @import("clang.zig"); | 22 | const clang = @import("clang.zig"); |
| 23 | const Cache = @import("Cache.zig"); | 23 | const Cache = std.Build.Cache; |
| 24 | const target_util = @import("target.zig"); | 24 | const target_util = @import("target.zig"); |
| 25 | const ThreadPool = @import("ThreadPool.zig"); | 25 | const ThreadPool = @import("ThreadPool.zig"); |
| 26 | const crash_report = @import("crash_report.zig"); | 26 | const crash_report = @import("crash_report.zig"); |
| ... | @@ -432,6 +432,8 @@ const usage_build_generic = | ... | @@ -432,6 +432,8 @@ const usage_build_generic = |
| 432 | \\ -fno-Clang Prevent using Clang as the C/C++ compilation backend | 432 | \\ -fno-Clang Prevent using Clang as the C/C++ compilation backend |
| 433 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error | 433 | \\ -freference-trace[=num] How many lines of reference trace should be shown per compile error |
| 434 | \\ -fno-reference-trace Disable reference trace | 434 | \\ -fno-reference-trace Disable reference trace |
| 435 | \\ -ferror-tracing Enable error tracing in ReleaseFast mode | ||
| 436 | \\ -fno-error-tracing Disable error tracing in Debug and ReleaseSafe mode | ||
| 435 | \\ -fsingle-threaded Code assumes there is only one thread | 437 | \\ -fsingle-threaded Code assumes there is only one thread |
| 436 | \\ -fno-single-threaded Code may not assume there is only one thread | 438 | \\ -fno-single-threaded Code may not assume there is only one thread |
| 437 | \\ -fbuiltin Enable implicit builtin knowledge of functions | 439 | \\ -fbuiltin Enable implicit builtin knowledge of functions |
| ... | @@ -490,6 +492,7 @@ const usage_build_generic = | ... | @@ -490,6 +492,7 @@ const usage_build_generic = |
| 490 | \\ nodelete Indicate that the object cannot be deleted from a process | 492 | \\ nodelete Indicate that the object cannot be deleted from a process |
| 491 | \\ notext Permit read-only relocations in read-only segments | 493 | \\ notext Permit read-only relocations in read-only segments |
| 492 | \\ defs Force a fatal error if any undefined symbols remain | 494 | \\ defs Force a fatal error if any undefined symbols remain |
| 495 | \\ undefs Reverse of -z defs | ||
| 493 | \\ origin Indicate that the object must have its origin processed | 496 | \\ origin Indicate that the object must have its origin processed |
| 494 | \\ nocopyreloc Disable the creation of copy relocations | 497 | \\ nocopyreloc Disable the creation of copy relocations |
| 495 | \\ now (default) Force all relocations to be processed on load | 498 | \\ now (default) Force all relocations to be processed on load |
| ... | @@ -506,6 +509,7 @@ const usage_build_generic = | ... | @@ -506,6 +509,7 @@ const usage_build_generic = |
| 506 | \\ zlib Compression with deflate/inflate | 509 | \\ zlib Compression with deflate/inflate |
| 507 | \\ --gc-sections Force removal of functions and data that are unreachable by the entry point or exported symbols | 510 | \\ --gc-sections Force removal of functions and data that are unreachable by the entry point or exported symbols |
| 508 | \\ --no-gc-sections Don't force removal of unreachable functions and data | 511 | \\ --no-gc-sections Don't force removal of unreachable functions and data |
| 512 | \\ --sort-section=[value] Sort wildcard section patterns by 'name' or 'alignment' | ||
| 509 | \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker | 513 | \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker |
| 510 | \\ --stack [size] Override default stack size | 514 | \\ --stack [size] Override default stack size |
| 511 | \\ --image-base [addr] Set base address for executable image | 515 | \\ --image-base [addr] Set base address for executable image |
| ... | @@ -728,6 +732,7 @@ fn buildOutputType( | ... | @@ -728,6 +732,7 @@ fn buildOutputType( |
| 728 | var linker_script: ?[]const u8 = null; | 732 | var linker_script: ?[]const u8 = null; |
| 729 | var version_script: ?[]const u8 = null; | 733 | var version_script: ?[]const u8 = null; |
| 730 | var disable_c_depfile = false; | 734 | var disable_c_depfile = false; |
| 735 | var linker_sort_section: ?link.SortSection = null; | ||
| 731 | var linker_gc_sections: ?bool = null; | 736 | var linker_gc_sections: ?bool = null; |
| 732 | var linker_compress_debug_sections: ?link.CompressDebugSections = null; | 737 | var linker_compress_debug_sections: ?link.CompressDebugSections = null; |
| 733 | var linker_allow_shlib_undefined: ?bool = null; | 738 | var linker_allow_shlib_undefined: ?bool = null; |
| ... | @@ -797,6 +802,7 @@ fn buildOutputType( | ... | @@ -797,6 +802,7 @@ fn buildOutputType( |
| 797 | var headerpad_max_install_names: bool = false; | 802 | var headerpad_max_install_names: bool = false; |
| 798 | var dead_strip_dylibs: bool = false; | 803 | var dead_strip_dylibs: bool = false; |
| 799 | var reference_trace: ?u32 = null; | 804 | var reference_trace: ?u32 = null; |
| 805 | var error_tracing: ?bool = null; | ||
| 800 | var pdb_out_path: ?[]const u8 = null; | 806 | var pdb_out_path: ?[]const u8 = null; |
| 801 | 807 | ||
| 802 | // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. | 808 | // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names. |
| ... | @@ -1218,6 +1224,10 @@ fn buildOutputType( | ... | @@ -1218,6 +1224,10 @@ fn buildOutputType( |
| 1218 | }; | 1224 | }; |
| 1219 | } else if (mem.eql(u8, arg, "-fno-reference-trace")) { | 1225 | } else if (mem.eql(u8, arg, "-fno-reference-trace")) { |
| 1220 | reference_trace = null; | 1226 | reference_trace = null; |
| 1227 | } else if (mem.eql(u8, arg, "-ferror-tracing")) { | ||
| 1228 | error_tracing = true; | ||
| 1229 | } else if (mem.eql(u8, arg, "-fno-error-tracing")) { | ||
| 1230 | error_tracing = false; | ||
| 1221 | } else if (mem.eql(u8, arg, "-rdynamic")) { | 1231 | } else if (mem.eql(u8, arg, "-rdynamic")) { |
| 1222 | rdynamic = true; | 1232 | rdynamic = true; |
| 1223 | } else if (mem.eql(u8, arg, "-fsoname")) { | 1233 | } else if (mem.eql(u8, arg, "-fsoname")) { |
| ... | @@ -1326,6 +1336,8 @@ fn buildOutputType( | ... | @@ -1326,6 +1336,8 @@ fn buildOutputType( |
| 1326 | linker_z_notext = true; | 1336 | linker_z_notext = true; |
| 1327 | } else if (mem.eql(u8, z_arg, "defs")) { | 1337 | } else if (mem.eql(u8, z_arg, "defs")) { |
| 1328 | linker_z_defs = true; | 1338 | linker_z_defs = true; |
| 1339 | } else if (mem.eql(u8, z_arg, "undefs")) { | ||
| 1340 | linker_z_defs = false; | ||
| 1329 | } else if (mem.eql(u8, z_arg, "origin")) { | 1341 | } else if (mem.eql(u8, z_arg, "origin")) { |
| 1330 | linker_z_origin = true; | 1342 | linker_z_origin = true; |
| 1331 | } else if (mem.eql(u8, z_arg, "nocopyreloc")) { | 1343 | } else if (mem.eql(u8, z_arg, "nocopyreloc")) { |
| ... | @@ -1343,7 +1355,7 @@ fn buildOutputType( | ... | @@ -1343,7 +1355,7 @@ fn buildOutputType( |
| 1343 | } else if (mem.startsWith(u8, z_arg, "max-page-size=")) { | 1355 | } else if (mem.startsWith(u8, z_arg, "max-page-size=")) { |
| 1344 | linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len); | 1356 | linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len); |
| 1345 | } else { | 1357 | } else { |
| 1346 | warn("unsupported linker extension flag: -z {s}", .{z_arg}); | 1358 | fatal("unsupported linker extension flag: -z {s}", .{z_arg}); |
| 1347 | } | 1359 | } |
| 1348 | } else if (mem.eql(u8, arg, "--import-memory")) { | 1360 | } else if (mem.eql(u8, arg, "--import-memory")) { |
| 1349 | linker_import_memory = true; | 1361 | linker_import_memory = true; |
| ... | @@ -1607,6 +1619,10 @@ fn buildOutputType( | ... | @@ -1607,6 +1619,10 @@ fn buildOutputType( |
| 1607 | build_id = true; | 1619 | build_id = true; |
| 1608 | warn("ignoring build-id style argument: '{s}'", .{value}); | 1620 | warn("ignoring build-id style argument: '{s}'", .{value}); |
| 1609 | continue; | 1621 | continue; |
| 1622 | } else if (mem.eql(u8, key, "--sort-common")) { | ||
| 1623 | // this ignores --sort=common=<anything>; ignoring plain --sort-common | ||
| 1624 | // is done below. | ||
| 1625 | continue; | ||
| 1610 | } | 1626 | } |
| 1611 | try linker_args.append(key); | 1627 | try linker_args.append(key); |
| 1612 | try linker_args.append(value); | 1628 | try linker_args.append(value); |
| ... | @@ -1619,6 +1635,9 @@ fn buildOutputType( | ... | @@ -1619,6 +1635,9 @@ fn buildOutputType( |
| 1619 | needed = true; | 1635 | needed = true; |
| 1620 | } else if (mem.eql(u8, linker_arg, "-no-pie")) { | 1636 | } else if (mem.eql(u8, linker_arg, "-no-pie")) { |
| 1621 | want_pie = false; | 1637 | want_pie = false; |
| 1638 | } else if (mem.eql(u8, linker_arg, "--sort-common")) { | ||
| 1639 | // from ld.lld(1): --sort-common is ignored for GNU compatibility, | ||
| 1640 | // this ignores plain --sort-common | ||
| 1622 | } else if (mem.eql(u8, linker_arg, "--whole-archive") or | 1641 | } else if (mem.eql(u8, linker_arg, "--whole-archive") or |
| 1623 | mem.eql(u8, linker_arg, "-whole-archive")) | 1642 | mem.eql(u8, linker_arg, "-whole-archive")) |
| 1624 | { | 1643 | { |
| ... | @@ -1872,6 +1891,8 @@ fn buildOutputType( | ... | @@ -1872,6 +1891,8 @@ fn buildOutputType( |
| 1872 | linker_gc_sections = true; | 1891 | linker_gc_sections = true; |
| 1873 | } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) { | 1892 | } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) { |
| 1874 | dead_strip_dylibs = true; | 1893 | dead_strip_dylibs = true; |
| 1894 | } else if (mem.eql(u8, arg, "--no-undefined")) { | ||
| 1895 | linker_z_defs = true; | ||
| 1875 | } else if (mem.eql(u8, arg, "--gc-sections")) { | 1896 | } else if (mem.eql(u8, arg, "--gc-sections")) { |
| 1876 | linker_gc_sections = true; | 1897 | linker_gc_sections = true; |
| 1877 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { | 1898 | } else if (mem.eql(u8, arg, "--no-gc-sections")) { |
| ... | @@ -1882,6 +1903,15 @@ fn buildOutputType( | ... | @@ -1882,6 +1903,15 @@ fn buildOutputType( |
| 1882 | linker_print_icf_sections = true; | 1903 | linker_print_icf_sections = true; |
| 1883 | } else if (mem.eql(u8, arg, "--print-map")) { | 1904 | } else if (mem.eql(u8, arg, "--print-map")) { |
| 1884 | linker_print_map = true; | 1905 | linker_print_map = true; |
| 1906 | } else if (mem.eql(u8, arg, "--sort-section")) { | ||
| 1907 | i += 1; | ||
| 1908 | if (i >= linker_args.items.len) { | ||
| 1909 | fatal("expected linker arg after '{s}'", .{arg}); | ||
| 1910 | } | ||
| 1911 | const arg1 = linker_args.items[i]; | ||
| 1912 | linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse { | ||
| 1913 | fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1}); | ||
| 1914 | }; | ||
| 1885 | } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or | 1915 | } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or |
| 1886 | mem.eql(u8, arg, "-allow-shlib-undefined")) | 1916 | mem.eql(u8, arg, "-allow-shlib-undefined")) |
| 1887 | { | 1917 | { |
| ... | @@ -1937,6 +1967,8 @@ fn buildOutputType( | ... | @@ -1937,6 +1967,8 @@ fn buildOutputType( |
| 1937 | linker_z_notext = true; | 1967 | linker_z_notext = true; |
| 1938 | } else if (mem.eql(u8, z_arg, "defs")) { | 1968 | } else if (mem.eql(u8, z_arg, "defs")) { |
| 1939 | linker_z_defs = true; | 1969 | linker_z_defs = true; |
| 1970 | } else if (mem.eql(u8, z_arg, "undefs")) { | ||
| 1971 | linker_z_defs = false; | ||
| 1940 | } else if (mem.eql(u8, z_arg, "origin")) { | 1972 | } else if (mem.eql(u8, z_arg, "origin")) { |
| 1941 | linker_z_origin = true; | 1973 | linker_z_origin = true; |
| 1942 | } else if (mem.eql(u8, z_arg, "nocopyreloc")) { | 1974 | } else if (mem.eql(u8, z_arg, "nocopyreloc")) { |
| ... | @@ -1961,7 +1993,7 @@ fn buildOutputType( | ... | @@ -1961,7 +1993,7 @@ fn buildOutputType( |
| 1961 | } else if (mem.startsWith(u8, z_arg, "max-page-size=")) { | 1993 | } else if (mem.startsWith(u8, z_arg, "max-page-size=")) { |
| 1962 | linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len); | 1994 | linker_z_max_page_size = parseIntSuffix(z_arg, "max-page-size=".len); |
| 1963 | } else { | 1995 | } else { |
| 1964 | warn("unsupported linker extension flag: -z {s}", .{z_arg}); | 1996 | fatal("unsupported linker extension flag: -z {s}", .{z_arg}); |
| 1965 | } | 1997 | } |
| 1966 | } else if (mem.eql(u8, arg, "--major-image-version")) { | 1998 | } else if (mem.eql(u8, arg, "--major-image-version")) { |
| 1967 | i += 1; | 1999 | i += 1; |
| ... | @@ -2023,6 +2055,14 @@ fn buildOutputType( | ... | @@ -2023,6 +2055,14 @@ fn buildOutputType( |
| 2023 | // This option does not do anything. | 2055 | // This option does not do anything. |
| 2024 | } else if (mem.eql(u8, arg, "--export-all-symbols")) { | 2056 | } else if (mem.eql(u8, arg, "--export-all-symbols")) { |
| 2025 | rdynamic = true; | 2057 | rdynamic = true; |
| 2058 | } else if (mem.eql(u8, arg, "--color-diagnostics") or | ||
| 2059 | mem.eql(u8, arg, "--color-diagnostics=always")) | ||
| 2060 | { | ||
| 2061 | color = .on; | ||
| 2062 | } else if (mem.eql(u8, arg, "--no-color-diagnostics") or | ||
| 2063 | mem.eql(u8, arg, "--color-diagnostics=never")) | ||
| 2064 | { | ||
| 2065 | color = .off; | ||
| 2026 | } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--strip-all") or | 2066 | } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--strip-all") or |
| 2027 | mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-debug")) | 2067 | mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-debug")) |
| 2028 | { | 2068 | { |
| ... | @@ -2186,7 +2226,7 @@ fn buildOutputType( | ... | @@ -2186,7 +2226,7 @@ fn buildOutputType( |
| 2186 | 2226 | ||
| 2187 | have_version = true; | 2227 | have_version = true; |
| 2188 | } else { | 2228 | } else { |
| 2189 | warn("unsupported linker arg: {s}", .{arg}); | 2229 | fatal("unsupported linker arg: {s}", .{arg}); |
| 2190 | } | 2230 | } |
| 2191 | } | 2231 | } |
| 2192 | 2232 | ||
| ... | @@ -3056,6 +3096,7 @@ fn buildOutputType( | ... | @@ -3056,6 +3096,7 @@ fn buildOutputType( |
| 3056 | .version_script = version_script, | 3096 | .version_script = version_script, |
| 3057 | .disable_c_depfile = disable_c_depfile, | 3097 | .disable_c_depfile = disable_c_depfile, |
| 3058 | .soname = resolved_soname, | 3098 | .soname = resolved_soname, |
| 3099 | .linker_sort_section = linker_sort_section, | ||
| 3059 | .linker_gc_sections = linker_gc_sections, | 3100 | .linker_gc_sections = linker_gc_sections, |
| 3060 | .linker_allow_shlib_undefined = linker_allow_shlib_undefined, | 3101 | .linker_allow_shlib_undefined = linker_allow_shlib_undefined, |
| 3061 | .linker_bind_global_refs_locally = linker_bind_global_refs_locally, | 3102 | .linker_bind_global_refs_locally = linker_bind_global_refs_locally, |
| ... | @@ -3136,6 +3177,7 @@ fn buildOutputType( | ... | @@ -3136,6 +3177,7 @@ fn buildOutputType( |
| 3136 | .headerpad_max_install_names = headerpad_max_install_names, | 3177 | .headerpad_max_install_names = headerpad_max_install_names, |
| 3137 | .dead_strip_dylibs = dead_strip_dylibs, | 3178 | .dead_strip_dylibs = dead_strip_dylibs, |
| 3138 | .reference_trace = reference_trace, | 3179 | .reference_trace = reference_trace, |
| 3180 | .error_tracing = error_tracing, | ||
| 3139 | .pdb_out_path = pdb_out_path, | 3181 | .pdb_out_path = pdb_out_path, |
| 3140 | }) catch |err| switch (err) { | 3182 | }) catch |err| switch (err) { |
| 3141 | error.LibCUnavailable => { | 3183 | error.LibCUnavailable => { |
| ... | @@ -3607,7 +3649,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void | ... | @@ -3607,7 +3649,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void |
| 3607 | defer if (enable_cache) man.deinit(); | 3649 | defer if (enable_cache) man.deinit(); |
| 3608 | 3650 | ||
| 3609 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects | 3651 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects |
| 3610 | man.hashCSource(c_source_file) catch |err| { | 3652 | Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { |
| 3611 | fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); | 3653 | fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); |
| 3612 | }; | 3654 | }; |
| 3613 | 3655 | ||
| ... | @@ -4523,7 +4565,7 @@ fn fmtPathDir( | ... | @@ -4523,7 +4565,7 @@ fn fmtPathDir( |
| 4523 | 4565 | ||
| 4524 | if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue; | 4566 | if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue; |
| 4525 | 4567 | ||
| 4526 | if (is_dir or entry.kind == .File and mem.endsWith(u8, entry.name, ".zig")) { | 4568 | if (is_dir or entry.kind == .File and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) { |
| 4527 | const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); | 4569 | const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); |
| 4528 | defer fmt.gpa.free(full_path); | 4570 | defer fmt.gpa.free(full_path); |
| 4529 | 4571 |
src/mingw.zig+1-1| ... | @@ -8,7 +8,7 @@ const log = std.log.scoped(.mingw); | ... | @@ -8,7 +8,7 @@ const log = std.log.scoped(.mingw); |
| 8 | const builtin = @import("builtin"); | 8 | const builtin = @import("builtin"); |
| 9 | const Compilation = @import("Compilation.zig"); | 9 | const Compilation = @import("Compilation.zig"); |
| 10 | const build_options = @import("build_options"); | 10 | const build_options = @import("build_options"); |
| 11 | const Cache = @import("Cache.zig"); | 11 | const Cache = std.Build.Cache; |
| 12 | 12 | ||
| 13 | pub const CRTFile = enum { | 13 | pub const CRTFile = enum { |
| 14 | crt2_o, | 14 | crt2_o, |
src/print_zir.zig+2-1| ... | @@ -332,7 +332,6 @@ const Writer = struct { | ... | @@ -332,7 +332,6 @@ const Writer = struct { |
| 332 | .float_cast, | 332 | .float_cast, |
| 333 | .int_cast, | 333 | .int_cast, |
| 334 | .ptr_cast, | 334 | .ptr_cast, |
| 335 | .qual_cast, | ||
| 336 | .truncate, | 335 | .truncate, |
| 337 | .align_cast, | 336 | .align_cast, |
| 338 | .div_exact, | 337 | .div_exact, |
| ... | @@ -507,6 +506,8 @@ const Writer = struct { | ... | @@ -507,6 +506,8 @@ const Writer = struct { |
| 507 | .reify, | 506 | .reify, |
| 508 | .c_va_copy, | 507 | .c_va_copy, |
| 509 | .c_va_end, | 508 | .c_va_end, |
| 509 | .const_cast, | ||
| 510 | .volatile_cast, | ||
| 510 | => { | 511 | => { |
| 511 | const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; | 512 | const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 512 | const src = LazySrcLoc.nodeOffset(inst_data.node); | 513 | const src = LazySrcLoc.nodeOffset(inst_data.node); |
src/translate_c.zig+2-1| ... | @@ -1748,7 +1748,8 @@ fn transBinaryOperator( | ... | @@ -1748,7 +1748,8 @@ fn transBinaryOperator( |
| 1748 | const lhs_expr = stmt.getLHS(); | 1748 | const lhs_expr = stmt.getLHS(); |
| 1749 | const lhs_qt = getExprQualType(c, lhs_expr); | 1749 | const lhs_qt = getExprQualType(c, lhs_expr); |
| 1750 | const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc()); | 1750 | const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc()); |
| 1751 | const elem_type = lhs_qt_translated.castTag(.c_pointer).?.data.elem_type; | 1751 | const c_pointer = getContainer(c, lhs_qt_translated).?; |
| 1752 | const elem_type = c_pointer.castTag(.c_pointer).?.data.elem_type; | ||
| 1752 | const sizeof = try Tag.sizeof.create(c.arena, elem_type); | 1753 | const sizeof = try Tag.sizeof.create(c.arena, elem_type); |
| 1753 | 1754 | ||
| 1754 | const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode }); | 1755 | const bitcast = try Tag.bit_cast.create(c.arena, .{ .lhs = ptrdiff_type, .rhs = infixOpNode }); |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ | |||
test/behavior/cast.zig+27| ... | @@ -1541,3 +1541,30 @@ test "single item pointer to pointer to array to slice" { | ... | @@ -1541,3 +1541,30 @@ test "single item pointer to pointer to array to slice" { |
| 1541 | const z1 = @as([]const i32, @as(*[1]i32, &x)); | 1541 | const z1 = @as([]const i32, @as(*[1]i32, &x)); |
| 1542 | try expect(z1[0] == 1234); | 1542 | try expect(z1[0] == 1234); |
| 1543 | } | 1543 | } |
| 1544 | |||
| 1545 | test "peer type resolution forms error union" { | ||
| 1546 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1547 | |||
| 1548 | var foo: i32 = 123; | ||
| 1549 | const result = if (foo < 0) switch (-foo) { | ||
| 1550 | 0 => unreachable, | ||
| 1551 | 42 => error.AccessDenied, | ||
| 1552 | else => unreachable, | ||
| 1553 | } else @intCast(u32, foo); | ||
| 1554 | try expect(try result == 123); | ||
| 1555 | } | ||
| 1556 | |||
| 1557 | test "@constCast without a result location" { | ||
| 1558 | const x: i32 = 1234; | ||
| 1559 | const y = @constCast(&x); | ||
| 1560 | try expect(@TypeOf(y) == *i32); | ||
| 1561 | try expect(y.* == 1234); | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | test "@volatileCast without a result location" { | ||
| 1565 | var x: i32 = 1234; | ||
| 1566 | var y: *volatile i32 = &x; | ||
| 1567 | const z = @volatileCast(y); | ||
| 1568 | try expect(@TypeOf(z) == *i32); | ||
| 1569 | try expect(z.* == 1234); | ||
| 1570 | } |
test/cases/compile_errors/function_returning_opaque_type.zig+6-6| ... | @@ -1,11 +1,11 @@ | ... | @@ -1,11 +1,11 @@ |
| 1 | const FooType = opaque {}; | 1 | const FooType = opaque {}; |
| 2 | export fn bar() !FooType { | 2 | export fn bar() FooType { |
| 3 | return error.InvalidValue; | 3 | return error.InvalidValue; |
| 4 | } | 4 | } |
| 5 | export fn bav() !@TypeOf(null) { | 5 | export fn bav() @TypeOf(null) { |
| 6 | return error.InvalidValue; | 6 | return error.InvalidValue; |
| 7 | } | 7 | } |
| 8 | export fn baz() !@TypeOf(undefined) { | 8 | export fn baz() @TypeOf(undefined) { |
| 9 | return error.InvalidValue; | 9 | return error.InvalidValue; |
| 10 | } | 10 | } |
| 11 | 11 | ||
| ... | @@ -13,7 +13,7 @@ export fn baz() !@TypeOf(undefined) { | ... | @@ -13,7 +13,7 @@ export fn baz() !@TypeOf(undefined) { |
| 13 | // backend=stage2 | 13 | // backend=stage2 |
| 14 | // target=native | 14 | // target=native |
| 15 | // | 15 | // |
| 16 | // :2:18: error: opaque return type 'tmp.FooType' not allowed | 16 | // :2:17: error: opaque return type 'tmp.FooType' not allowed |
| 17 | // :1:17: note: opaque declared here | 17 | // :1:17: note: opaque declared here |
| 18 | // :5:18: error: return type '@TypeOf(null)' not allowed | 18 | // :5:17: error: return type '@TypeOf(null)' not allowed |
| 19 | // :8:18: error: return type '@TypeOf(undefined)' not allowed | 19 | // :8:17: error: return type '@TypeOf(undefined)' not allowed |
test/cases/compile_errors/invalid_error_union_payload_type.zig+7| ... | @@ -4,6 +4,12 @@ comptime { | ... | @@ -4,6 +4,12 @@ comptime { |
| 4 | comptime { | 4 | comptime { |
| 5 | _ = anyerror!anyerror; | 5 | _ = anyerror!anyerror; |
| 6 | } | 6 | } |
| 7 | fn someFunction() !anyerror { | ||
| 8 | return error.C; | ||
| 9 | } | ||
| 10 | comptime { | ||
| 11 | _ = someFunction; | ||
| 12 | } | ||
| 7 | 13 | ||
| 8 | // error | 14 | // error |
| 9 | // backend=stage2 | 15 | // backend=stage2 |
| ... | @@ -11,3 +17,4 @@ comptime { | ... | @@ -11,3 +17,4 @@ comptime { |
| 11 | // | 17 | // |
| 12 | // :2:18: error: error union with payload of opaque type 'anyopaque' not allowed | 18 | // :2:18: error: error union with payload of opaque type 'anyopaque' not allowed |
| 13 | // :5:18: error: error union with payload of error set type 'anyerror' not allowed | 19 | // :5:18: error: error union with payload of error set type 'anyerror' not allowed |
| 20 | // :7:20: error: error union with payload of error set type 'anyerror' not allowed |
test/cases/compile_errors/invalid_qualcast.zig deleted-12| ... | @@ -1,12 +0,0 @@ | ||
| 1 | pub export fn entry() void { | ||
| 2 | var a: [*:0]const volatile u16 = undefined; | ||
| 3 | _ = @qualCast([*]u16, a); | ||
| 4 | } | ||
| 5 | |||
| 6 | // error | ||
| 7 | // backend=stage2 | ||
| 8 | // target=native | ||
| 9 | // | ||
| 10 | // :3:9: error: '@qualCast' can only modify 'const' and 'volatile' qualifiers | ||
| 11 | // :3:9: note: expected type '[*]const volatile u16' | ||
| 12 | // :3:9: note: got type '[*:0]const volatile u16' | ||
test/cases/compile_errors/ptrCast_discards_const_qualifier.zig+1-1| ... | @@ -9,4 +9,4 @@ export fn entry() void { | ... | @@ -9,4 +9,4 @@ export fn entry() void { |
| 9 | // target=native | 9 | // target=native |
| 10 | // | 10 | // |
| 11 | // :3:15: error: cast discards const qualifier | 11 | // :3:15: error: cast discards const qualifier |
| 12 | // :3:15: note: consider using '@qualCast' | 12 | // :3:15: note: consider using '@constCast' |
test/link/macho/uuid/build.zig+132-15| ... | @@ -1,4 +1,8 @@ | ... | @@ -1,4 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Builder = std.Build.Builder; | ||
| 3 | const CompileStep = std.Build.CompileStep; | ||
| 4 | const FileSource = std.Build.FileSource; | ||
| 5 | const Step = std.Build.Step; | ||
| 2 | 6 | ||
| 3 | pub fn build(b: *std.Build) void { | 7 | pub fn build(b: *std.Build) void { |
| 4 | const test_step = b.step("test", "Test"); | 8 | const test_step = b.step("test", "Test"); |
| ... | @@ -10,18 +14,18 @@ pub fn build(b: *std.Build) void { | ... | @@ -10,18 +14,18 @@ pub fn build(b: *std.Build) void { |
| 10 | .os_tag = .macos, | 14 | .os_tag = .macos, |
| 11 | }; | 15 | }; |
| 12 | 16 | ||
| 13 | testUuid(b, test_step, .ReleaseSafe, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9"); | 17 | testUuid(b, test_step, .ReleaseSafe, aarch64_macos); |
| 14 | testUuid(b, test_step, .ReleaseFast, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9"); | 18 | testUuid(b, test_step, .ReleaseFast, aarch64_macos); |
| 15 | testUuid(b, test_step, .ReleaseSmall, aarch64_macos, "675bb6ba8e5d3d3191f7936d7168f0e9"); | 19 | testUuid(b, test_step, .ReleaseSmall, aarch64_macos); |
| 16 | 20 | ||
| 17 | const x86_64_macos = std.zig.CrossTarget{ | 21 | const x86_64_macos = std.zig.CrossTarget{ |
| 18 | .cpu_arch = .x86_64, | 22 | .cpu_arch = .x86_64, |
| 19 | .os_tag = .macos, | 23 | .os_tag = .macos, |
| 20 | }; | 24 | }; |
| 21 | 25 | ||
| 22 | testUuid(b, test_step, .ReleaseSafe, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48"); | 26 | testUuid(b, test_step, .ReleaseSafe, x86_64_macos); |
| 23 | testUuid(b, test_step, .ReleaseFast, x86_64_macos, "5b7071b4587c3071b0d2352fadce0e48"); | 27 | testUuid(b, test_step, .ReleaseFast, x86_64_macos); |
| 24 | testUuid(b, test_step, .ReleaseSmall, x86_64_macos, "4b58f2583c383169bbe3a716bd240048"); | 28 | testUuid(b, test_step, .ReleaseSmall, x86_64_macos); |
| 25 | } | 29 | } |
| 26 | 30 | ||
| 27 | fn testUuid( | 31 | fn testUuid( |
| ... | @@ -29,25 +33,23 @@ fn testUuid( | ... | @@ -29,25 +33,23 @@ fn testUuid( |
| 29 | test_step: *std.Build.Step, | 33 | test_step: *std.Build.Step, |
| 30 | optimize: std.builtin.OptimizeMode, | 34 | optimize: std.builtin.OptimizeMode, |
| 31 | target: std.zig.CrossTarget, | 35 | target: std.zig.CrossTarget, |
| 32 | comptime exp: []const u8, | ||
| 33 | ) void { | 36 | ) void { |
| 34 | // The calculated UUID value is independent of debug info and so it should | 37 | // The calculated UUID value is independent of debug info and so it should |
| 35 | // stay the same across builds. | 38 | // stay the same across builds. |
| 36 | { | 39 | { |
| 37 | const dylib = simpleDylib(b, optimize, target); | 40 | const dylib = simpleDylib(b, optimize, target); |
| 38 | const check_dylib = dylib.checkObject(.macho); | 41 | const install_step = installWithRename(dylib, "test1.dylib"); |
| 39 | check_dylib.checkStart("cmd UUID"); | 42 | install_step.step.dependOn(&dylib.step); |
| 40 | check_dylib.checkNext("uuid " ++ exp); | ||
| 41 | test_step.dependOn(&check_dylib.step); | ||
| 42 | } | 43 | } |
| 43 | { | 44 | { |
| 44 | const dylib = simpleDylib(b, optimize, target); | 45 | const dylib = simpleDylib(b, optimize, target); |
| 45 | dylib.strip = true; | 46 | dylib.strip = true; |
| 46 | const check_dylib = dylib.checkObject(.macho); | 47 | const install_step = installWithRename(dylib, "test2.dylib"); |
| 47 | check_dylib.checkStart("cmd UUID"); | 48 | install_step.step.dependOn(&dylib.step); |
| 48 | check_dylib.checkNext("uuid " ++ exp); | ||
| 49 | test_step.dependOn(&check_dylib.step); | ||
| 50 | } | 49 | } |
| 50 | |||
| 51 | const cmp_step = CompareUuid.create(b, "test1.dylib", "test2.dylib"); | ||
| 52 | test_step.dependOn(&cmp_step.step); | ||
| 51 | } | 53 | } |
| 52 | 54 | ||
| 53 | fn simpleDylib( | 55 | fn simpleDylib( |
| ... | @@ -65,3 +67,118 @@ fn simpleDylib( | ... | @@ -65,3 +67,118 @@ fn simpleDylib( |
| 65 | dylib.linkLibC(); | 67 | dylib.linkLibC(); |
| 66 | return dylib; | 68 | return dylib; |
| 67 | } | 69 | } |
| 70 | |||
| 71 | fn installWithRename(cs: *CompileStep, name: []const u8) *InstallWithRename { | ||
| 72 | const step = InstallWithRename.create(cs.builder, cs.getOutputSource(), name); | ||
| 73 | cs.builder.getInstallStep().dependOn(&step.step); | ||
| 74 | return step; | ||
| 75 | } | ||
| 76 | |||
| 77 | const InstallWithRename = struct { | ||
| 78 | pub const base_id = .custom; | ||
| 79 | |||
| 80 | step: Step, | ||
| 81 | builder: *Builder, | ||
| 82 | source: FileSource, | ||
| 83 | name: []const u8, | ||
| 84 | |||
| 85 | pub fn create( | ||
| 86 | builder: *Builder, | ||
| 87 | source: FileSource, | ||
| 88 | name: []const u8, | ||
| 89 | ) *InstallWithRename { | ||
| 90 | const self = builder.allocator.create(InstallWithRename) catch @panic("OOM"); | ||
| 91 | self.* = InstallWithRename{ | ||
| 92 | .builder = builder, | ||
| 93 | .step = Step.init(.custom, builder.fmt("install and rename: {s} -> {s}", .{ | ||
| 94 | source.getDisplayName(), | ||
| 95 | name, | ||
| 96 | }), builder.allocator, make), | ||
| 97 | .source = source, | ||
| 98 | .name = builder.dupe(name), | ||
| 99 | }; | ||
| 100 | return self; | ||
| 101 | } | ||
| 102 | |||
| 103 | fn make(step: *Step) anyerror!void { | ||
| 104 | const self = @fieldParentPtr(InstallWithRename, "step", step); | ||
| 105 | const source_path = self.source.getPath(self.builder); | ||
| 106 | const target_path = self.builder.getInstallPath(.lib, self.name); | ||
| 107 | self.builder.updateFile(source_path, target_path) catch |err| { | ||
| 108 | std.log.err("Unable to rename: {s} -> {s}", .{ source_path, target_path }); | ||
| 109 | return err; | ||
| 110 | }; | ||
| 111 | } | ||
| 112 | }; | ||
| 113 | |||
| 114 | const CompareUuid = struct { | ||
| 115 | pub const base_id = .custom; | ||
| 116 | |||
| 117 | step: Step, | ||
| 118 | builder: *Builder, | ||
| 119 | lhs: []const u8, | ||
| 120 | rhs: []const u8, | ||
| 121 | |||
| 122 | pub fn create(builder: *Builder, lhs: []const u8, rhs: []const u8) *CompareUuid { | ||
| 123 | const self = builder.allocator.create(CompareUuid) catch @panic("OOM"); | ||
| 124 | self.* = CompareUuid{ | ||
| 125 | .builder = builder, | ||
| 126 | .step = Step.init( | ||
| 127 | .custom, | ||
| 128 | builder.fmt("compare uuid: {s} and {s}", .{ | ||
| 129 | lhs, | ||
| 130 | rhs, | ||
| 131 | }), | ||
| 132 | builder.allocator, | ||
| 133 | make, | ||
| 134 | ), | ||
| 135 | .lhs = lhs, | ||
| 136 | .rhs = rhs, | ||
| 137 | }; | ||
| 138 | return self; | ||
| 139 | } | ||
| 140 | |||
| 141 | fn make(step: *Step) anyerror!void { | ||
| 142 | const self = @fieldParentPtr(CompareUuid, "step", step); | ||
| 143 | const gpa = self.builder.allocator; | ||
| 144 | |||
| 145 | var lhs_uuid: [16]u8 = undefined; | ||
| 146 | const lhs_path = self.builder.getInstallPath(.lib, self.lhs); | ||
| 147 | try parseUuid(gpa, lhs_path, &lhs_uuid); | ||
| 148 | |||
| 149 | var rhs_uuid: [16]u8 = undefined; | ||
| 150 | const rhs_path = self.builder.getInstallPath(.lib, self.rhs); | ||
| 151 | try parseUuid(gpa, rhs_path, &rhs_uuid); | ||
| 152 | |||
| 153 | try std.testing.expectEqualStrings(&lhs_uuid, &rhs_uuid); | ||
| 154 | } | ||
| 155 | |||
| 156 | fn parseUuid(gpa: std.mem.Allocator, path: []const u8, uuid: *[16]u8) anyerror!void { | ||
| 157 | const max_bytes: usize = 20 * 1024 * 1024; | ||
| 158 | const data = try std.fs.cwd().readFileAllocOptions( | ||
| 159 | gpa, | ||
| 160 | path, | ||
| 161 | max_bytes, | ||
| 162 | null, | ||
| 163 | @alignOf(u64), | ||
| 164 | null, | ||
| 165 | ); | ||
| 166 | var stream = std.io.fixedBufferStream(data); | ||
| 167 | const reader = stream.reader(); | ||
| 168 | |||
| 169 | const hdr = try reader.readStruct(std.macho.mach_header_64); | ||
| 170 | if (hdr.magic != std.macho.MH_MAGIC_64) { | ||
| 171 | return error.InvalidMagicNumber; | ||
| 172 | } | ||
| 173 | |||
| 174 | var it = std.macho.LoadCommandIterator{ | ||
| 175 | .ncmds = hdr.ncmds, | ||
| 176 | .buffer = data[@sizeOf(std.macho.mach_header_64)..][0..hdr.sizeofcmds], | ||
| 177 | }; | ||
| 178 | const cmd = while (it.next()) |cmd| switch (cmd.cmd()) { | ||
| 179 | .UUID => break cmd.cast(std.macho.uuid_command).?, | ||
| 180 | else => {}, | ||
| 181 | } else return error.UuidLoadCommandNotFound; | ||
| 182 | std.mem.copy(u8, uuid, &cmd.uuid); | ||
| 183 | } | ||
| 184 | }; |
test/standalone.zig+2| ... | @@ -27,6 +27,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -27,6 +27,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 27 | cases.add("test/standalone/noreturn_call/inline.zig"); | 27 | cases.add("test/standalone/noreturn_call/inline.zig"); |
| 28 | cases.add("test/standalone/noreturn_call/as_arg.zig"); | 28 | cases.add("test/standalone/noreturn_call/as_arg.zig"); |
| 29 | cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); | 29 | cases.addBuildFile("test/standalone/test_runner_path/build.zig", .{ .requires_stage2 = true }); |
| 30 | cases.addBuildFile("test/standalone/issue_13970/build.zig", .{}); | ||
| 30 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); | 31 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); |
| 31 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); | 32 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); |
| 32 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); | 33 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); |
| ... | @@ -102,4 +103,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -102,4 +103,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 102 | cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true }); | 103 | cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true }); |
| 103 | cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); | 104 | cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); |
| 104 | cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); | 105 | cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); |
| 106 | cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); | ||
| 105 | } | 107 | } |
test/standalone/embed_generated_file/bootloader.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | pub export fn _start() void {} | ||
test/standalone/embed_generated_file/build.zig created+28| ... | @@ -0,0 +1,28 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const target = b.standardTargetOptions(.{}); | ||
| 5 | const optimize = b.standardOptimizeOption(.{}); | ||
| 6 | |||
| 7 | const bootloader = b.addExecutable(.{ | ||
| 8 | .name = "bootloader", | ||
| 9 | .root_source_file = .{ .path = "bootloader.zig" }, | ||
| 10 | .target = .{ | ||
| 11 | .cpu_arch = .x86, | ||
| 12 | .os_tag = .freestanding, | ||
| 13 | }, | ||
| 14 | .optimize = .ReleaseSmall, | ||
| 15 | }); | ||
| 16 | |||
| 17 | const exe = b.addTest(.{ | ||
| 18 | .root_source_file = .{ .path = "main.zig" }, | ||
| 19 | .target = target, | ||
| 20 | .optimize = optimize, | ||
| 21 | }); | ||
| 22 | exe.addAnonymousModule("bootloader.elf", .{ | ||
| 23 | .source_file = bootloader.getOutputSource(), | ||
| 24 | }); | ||
| 25 | |||
| 26 | const test_step = b.step("test", "Test the program"); | ||
| 27 | test_step.dependOn(&exe.step); | ||
| 28 | } | ||
test/standalone/embed_generated_file/main.zig created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const blah = @embedFile("bootloader.elf"); | ||
| 3 | |||
| 4 | test { | ||
| 5 | comptime { | ||
| 6 | std.debug.assert(std.mem.eql(u8, blah[1..][0..3], "ELF")); | ||
| 7 | } | ||
| 8 | } | ||
test/standalone/issue_13970/build.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const test1 = b.addTest(.{ | ||
| 5 | .root_source_file = .{ .path = "test_root/empty.zig" }, | ||
| 6 | }); | ||
| 7 | const test2 = b.addTest(.{ | ||
| 8 | .root_source_file = .{ .path = "src/empty.zig" }, | ||
| 9 | }); | ||
| 10 | const test3 = b.addTest(.{ | ||
| 11 | .root_source_file = .{ .path = "empty.zig" }, | ||
| 12 | }); | ||
| 13 | test1.setTestRunner("src/main.zig"); | ||
| 14 | test2.setTestRunner("src/main.zig"); | ||
| 15 | test3.setTestRunner("src/main.zig"); | ||
| 16 | |||
| 17 | const test_step = b.step("test", "Test package path resolution of custom test runner"); | ||
| 18 | test_step.dependOn(&test1.step); | ||
| 19 | test_step.dependOn(&test2.step); | ||
| 20 | test_step.dependOn(&test3.step); | ||
| 21 | } | ||
test/standalone/issue_13970/empty.zig createdtest/standalone/issue_13970/src/empty.zig createdtest/standalone/issue_13970/src/main.zig created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const package = @import("package.zig"); | ||
| 3 | const root = @import("root"); | ||
| 4 | const builtin = @import("builtin"); | ||
| 5 | |||
| 6 | pub fn main() !void { | ||
| 7 | _ = package.decl; | ||
| 8 | } | ||
test/standalone/issue_13970/src/package.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | pub const decl = 0; | ||
test/standalone/issue_13970/test_root/empty.zig createdtest/standalone/test_runner_module_imports/build.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn build(b: *std.Build) void { | ||
| 4 | const t = b.addTest(.{ | ||
| 5 | .root_source_file = .{ .path = "src/main.zig" }, | ||
| 6 | }); | ||
| 7 | t.setTestRunner("test_runner/main.zig"); | ||
| 8 | |||
| 9 | const module1 = b.createModule(.{ .source_file = .{ .path = "module1/main.zig" } }); | ||
| 10 | const module2 = b.createModule(.{ | ||
| 11 | .source_file = .{ .path = "module2/main.zig" }, | ||
| 12 | .dependencies = &.{.{ .name = "module1", .module = module1 }}, | ||
| 13 | }); | ||
| 14 | |||
| 15 | t.addModule("module2", module2); | ||
| 16 | |||
| 17 | const test_step = b.step("test", "Run unit tests"); | ||
| 18 | test_step.dependOn(&t.step); | ||
| 19 | } | ||
test/standalone/test_runner_module_imports/module1/main.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | pub const decl: usize = 1234567890; | ||
test/standalone/test_runner_module_imports/module2/main.zig created+1| ... | @@ -0,0 +1 @@ | ||
| 1 | pub const mod1 = @import("module1"); | ||
test/standalone/test_runner_module_imports/src/main.zig created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | const mod2 = @import("module2"); | ||
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | test { | ||
| 5 | try std.testing.expectEqual(@as(usize, 1234567890), mod2.mod1.decl); | ||
| 6 | } | ||
test/standalone/test_runner_module_imports/test_runner/main.zig created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mod2 = @import("module2"); | ||
| 3 | |||
| 4 | pub fn main() !void { | ||
| 5 | try std.testing.expectEqual(@as(usize, 1234567890), mod2.mod1.decl); | ||
| 6 | for (@import("builtin").test_functions) |test_fn| { | ||
| 7 | try test_fn.func(); | ||
| 8 | } | ||
| 9 | } | ||
test/tests.zig+2-2| ... | @@ -570,7 +570,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co | ... | @@ -570,7 +570,7 @@ pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []co |
| 570 | const run_cmd = exe.run(); | 570 | const run_cmd = exe.run(); |
| 571 | run_cmd.addArgs(&[_][]const u8{ | 571 | run_cmd.addArgs(&[_][]const u8{ |
| 572 | fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, | 572 | fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, |
| 573 | b.pathFromRoot(b.cache_root), | 573 | b.pathFromRoot(b.cache_root.path orelse "."), |
| 574 | }); | 574 | }); |
| 575 | 575 | ||
| 576 | step.dependOn(&run_cmd.step); | 576 | step.dependOn(&run_cmd.step); |
| ... | @@ -1059,7 +1059,7 @@ pub const StandaloneContext = struct { | ... | @@ -1059,7 +1059,7 @@ pub const StandaloneContext = struct { |
| 1059 | } | 1059 | } |
| 1060 | 1060 | ||
| 1061 | var zig_args = ArrayList([]const u8).init(b.allocator); | 1061 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 1062 | const rel_zig_exe = fs.path.relative(b.allocator, b.build_root, b.zig_exe) catch unreachable; | 1062 | const rel_zig_exe = fs.path.relative(b.allocator, b.build_root.path orelse ".", b.zig_exe) catch unreachable; |
| 1063 | zig_args.append(rel_zig_exe) catch unreachable; | 1063 | zig_args.append(rel_zig_exe) catch unreachable; |
| 1064 | zig_args.append("build") catch unreachable; | 1064 | zig_args.append("build") catch unreachable; |
| 1065 | 1065 |
test/translate_c.zig+32| ... | @@ -3916,4 +3916,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3916,4 +3916,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3916 | \\ }) != 0) {} | 3916 | \\ }) != 0) {} |
| 3917 | \\} | 3917 | \\} |
| 3918 | }); | 3918 | }); |
| 3919 | |||
| 3920 | if (builtin.os.tag == .windows) { | ||
| 3921 | cases.add("Pointer subtraction with typedef", | ||
| 3922 | \\typedef char* S; | ||
| 3923 | \\void foo() { | ||
| 3924 | \\ S a, b; | ||
| 3925 | \\ long long c = a - b; | ||
| 3926 | \\} | ||
| 3927 | , &[_][]const u8{ | ||
| 3928 | \\pub export fn foo() void { | ||
| 3929 | \\ var a: S = undefined; | ||
| 3930 | \\ var b: S = undefined; | ||
| 3931 | \\ var c: c_longlong = @divExact(@bitCast(c_longlong, @ptrToInt(a) -% @ptrToInt(b)), @sizeOf(u8)); | ||
| 3932 | \\ _ = @TypeOf(c); | ||
| 3933 | \\} | ||
| 3934 | }); | ||
| 3935 | } else { | ||
| 3936 | cases.add("Pointer subtraction with typedef", | ||
| 3937 | \\typedef char* S; | ||
| 3938 | \\void foo() { | ||
| 3939 | \\ S a, b; | ||
| 3940 | \\ long c = a - b; | ||
| 3941 | \\} | ||
| 3942 | , &[_][]const u8{ | ||
| 3943 | \\pub export fn foo() void { | ||
| 3944 | \\ var a: S = undefined; | ||
| 3945 | \\ var b: S = undefined; | ||
| 3946 | \\ var c: c_long = @divExact(@bitCast(c_long, @ptrToInt(a) -% @ptrToInt(b)), @sizeOf(u8)); | ||
| 3947 | \\ _ = @TypeOf(c); | ||
| 3948 | \\} | ||
| 3949 | }); | ||
| 3950 | } | ||
| 3919 | } | 3951 | } |
tools/gen_stubs.zig+9| ... | @@ -592,8 +592,10 @@ const blacklisted_symbols = [_][]const u8{ | ... | @@ -592,8 +592,10 @@ const blacklisted_symbols = [_][]const u8{ |
| 592 | "__addtf3", | 592 | "__addtf3", |
| 593 | "__addxf3", | 593 | "__addxf3", |
| 594 | "__ashldi3", | 594 | "__ashldi3", |
| 595 | "__ashlsi3", | ||
| 595 | "__ashlti3", | 596 | "__ashlti3", |
| 596 | "__ashrdi3", | 597 | "__ashrdi3", |
| 598 | "__ashrsi3", | ||
| 597 | "__ashrti3", | 599 | "__ashrti3", |
| 598 | "__atomic_compare_exchange", | 600 | "__atomic_compare_exchange", |
| 599 | "__atomic_compare_exchange_1", | 601 | "__atomic_compare_exchange_1", |
| ... | @@ -664,6 +666,7 @@ const blacklisted_symbols = [_][]const u8{ | ... | @@ -664,6 +666,7 @@ const blacklisted_symbols = [_][]const u8{ |
| 664 | "__divkf3", | 666 | "__divkf3", |
| 665 | "__divmoddi4", | 667 | "__divmoddi4", |
| 666 | "__divmodsi4", | 668 | "__divmodsi4", |
| 669 | "__divmodti4", | ||
| 667 | "__divsf3", | 670 | "__divsf3", |
| 668 | "__divsi3", | 671 | "__divsi3", |
| 669 | "__divtf3", | 672 | "__divtf3", |
| ... | @@ -786,6 +789,7 @@ const blacklisted_symbols = [_][]const u8{ | ... | @@ -786,6 +789,7 @@ const blacklisted_symbols = [_][]const u8{ |
| 786 | "__logh", | 789 | "__logh", |
| 787 | "__logx", | 790 | "__logx", |
| 788 | "__lshrdi3", | 791 | "__lshrdi3", |
| 792 | "__lshrsi3", | ||
| 789 | "__lshrti3", | 793 | "__lshrti3", |
| 790 | "__ltdf2", | 794 | "__ltdf2", |
| 791 | "__ltkf2", | 795 | "__ltkf2", |
| ... | @@ -829,6 +833,11 @@ const blacklisted_symbols = [_][]const u8{ | ... | @@ -829,6 +833,11 @@ const blacklisted_symbols = [_][]const u8{ |
| 829 | "__popcountdi2", | 833 | "__popcountdi2", |
| 830 | "__popcountsi2", | 834 | "__popcountsi2", |
| 831 | "__popcountti2", | 835 | "__popcountti2", |
| 836 | "__powidf2", | ||
| 837 | "__powihf2", | ||
| 838 | "__powisf2", | ||
| 839 | "__powitf2", | ||
| 840 | "__powixf2", | ||
| 832 | "__roundh", | 841 | "__roundh", |
| 833 | "__roundx", | 842 | "__roundx", |
| 834 | "__sincosh", | 843 | "__sincosh", |
tools/generate_linux_syscalls.zig+25| ... | @@ -167,6 +167,31 @@ pub fn main() !void { | ... | @@ -167,6 +167,31 @@ pub fn main() !void { |
| 167 | 167 | ||
| 168 | try writer.writeAll("};\n\n"); | 168 | try writer.writeAll("};\n\n"); |
| 169 | } | 169 | } |
| 170 | { | ||
| 171 | try writer.writeAll( | ||
| 172 | \\pub const Mips64 = enum(usize) { | ||
| 173 | \\ pub const Linux = 5000; | ||
| 174 | \\ | ||
| 175 | \\ | ||
| 176 | ); | ||
| 177 | |||
| 178 | const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_n64.tbl", buf); | ||
| 179 | var lines = mem.tokenize(u8, table, "\n"); | ||
| 180 | while (lines.next()) |line| { | ||
| 181 | if (line[0] == '#') continue; | ||
| 182 | |||
| 183 | var fields = mem.tokenize(u8, line, " \t"); | ||
| 184 | const number = fields.next() orelse return error.Incomplete; | ||
| 185 | // abi is always n64 | ||
| 186 | _ = fields.next() orelse return error.Incomplete; | ||
| 187 | const name = fields.next() orelse return error.Incomplete; | ||
| 188 | const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name; | ||
| 189 | |||
| 190 | try writer.print(" {s} = Linux + {s},\n", .{ zig.fmtId(fixed_name), number }); | ||
| 191 | } | ||
| 192 | |||
| 193 | try writer.writeAll("};\n\n"); | ||
| 194 | } | ||
| 170 | { | 195 | { |
| 171 | try writer.writeAll("pub const PowerPC = enum(usize) {\n"); | 196 | try writer.writeAll("pub const PowerPC = enum(usize) {\n"); |
| 172 | 197 |
tools/update_cpu_features.zig+1-1| ... | @@ -1385,7 +1385,7 @@ fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { | ... | @@ -1385,7 +1385,7 @@ fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { |
| 1385 | \\ | 1385 | \\ |
| 1386 | \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td . | 1386 | \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td . |
| 1387 | \\ | 1387 | \\ |
| 1388 | \\On a less beefy system, or when debugging, compile with --single-threaded. | 1388 | \\On a less beefy system, or when debugging, compile with -fsingle-threaded. |
| 1389 | \\ | 1389 | \\ |
| 1390 | , .{arg0}) catch std.process.exit(1); | 1390 | , .{arg0}) catch std.process.exit(1); |
| 1391 | std.process.exit(code); | 1391 | std.process.exit(code); |