| author | |
| committer | |
| log | dbde8254d02241d893140cb499ad25e8cbb3438a |
| tree | af5be3723d32a378891fac4320e6a4b9a4edd674 |
| parent | f7f11e237c96a357e9a5e8b4a8ce2c6a7499de3b |
| parent | 2bd2a8ea3430b92f0c41d602d12982f776a9a524 |
| signature |
62 files changed, 4721 insertions(+), 964 deletions(-)
CMakeLists.txt+6-2| ... | @@ -447,13 +447,17 @@ set(ZIG_STD_FILES | ... | @@ -447,13 +447,17 @@ set(ZIG_STD_FILES |
| 447 | "c/index.zig" | 447 | "c/index.zig" |
| 448 | "c/linux.zig" | 448 | "c/linux.zig" |
| 449 | "c/windows.zig" | 449 | "c/windows.zig" |
| 450 | "coff.zig" | ||
| 450 | "crypto/blake2.zig" | 451 | "crypto/blake2.zig" |
| 452 | "crypto/chacha20.zig" | ||
| 451 | "crypto/hmac.zig" | 453 | "crypto/hmac.zig" |
| 452 | "crypto/index.zig" | 454 | "crypto/index.zig" |
| 453 | "crypto/md5.zig" | 455 | "crypto/md5.zig" |
| 454 | "crypto/sha1.zig" | 456 | "crypto/sha1.zig" |
| 455 | "crypto/sha2.zig" | 457 | "crypto/sha2.zig" |
| 456 | "crypto/sha3.zig" | 458 | "crypto/sha3.zig" |
| 459 | "crypto/poly1305.zig" | ||
| 460 | "crypto/x25519.zig" | ||
| 457 | "cstr.zig" | 461 | "cstr.zig" |
| 458 | "debug/failing_allocator.zig" | 462 | "debug/failing_allocator.zig" |
| 459 | "debug/index.zig" | 463 | "debug/index.zig" |
| ... | @@ -579,12 +583,12 @@ set(ZIG_STD_FILES | ... | @@ -579,12 +583,12 @@ set(ZIG_STD_FILES |
| 579 | "os/windows/error.zig" | 583 | "os/windows/error.zig" |
| 580 | "os/windows/index.zig" | 584 | "os/windows/index.zig" |
| 581 | "os/windows/kernel32.zig" | 585 | "os/windows/kernel32.zig" |
| 586 | "os/windows/ntdll.zig" | ||
| 582 | "os/windows/ole32.zig" | 587 | "os/windows/ole32.zig" |
| 583 | "os/windows/shell32.zig" | 588 | "os/windows/shell32.zig" |
| 584 | "os/windows/shlwapi.zig" | ||
| 585 | "os/windows/user32.zig" | ||
| 586 | "os/windows/util.zig" | 589 | "os/windows/util.zig" |
| 587 | "os/zen.zig" | 590 | "os/zen.zig" |
| 591 | "pdb.zig" | ||
| 588 | "rand/index.zig" | 592 | "rand/index.zig" |
| 589 | "rand/ziggurat.zig" | 593 | "rand/ziggurat.zig" |
| 590 | "segmented_list.zig" | 594 | "segmented_list.zig" |
doc/docgen.zig+2-2| ... | @@ -40,11 +40,11 @@ pub fn main() !void { | ... | @@ -40,11 +40,11 @@ pub fn main() !void { |
| 40 | var out_file = try os.File.openWrite(out_file_name); | 40 | var out_file = try os.File.openWrite(out_file_name); |
| 41 | defer out_file.close(); | 41 | defer out_file.close(); |
| 42 | 42 | ||
| 43 | var file_in_stream = io.FileInStream.init(&in_file); | 43 | var file_in_stream = io.FileInStream.init(in_file); |
| 44 | 44 | ||
| 45 | const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size); | 45 | const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size); |
| 46 | 46 | ||
| 47 | var file_out_stream = io.FileOutStream.init(&out_file); | 47 | var file_out_stream = io.FileOutStream.init(out_file); |
| 48 | var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream); | 48 | var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream); |
| 49 | 49 | ||
| 50 | var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); | 50 | var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); |
doc/langref.html.in+170-2| ... | @@ -566,7 +566,7 @@ const c_string_literal = | ... | @@ -566,7 +566,7 @@ const c_string_literal = |
| 566 | {#header_close#} | 566 | {#header_close#} |
| 567 | {#header_close#} | 567 | {#header_close#} |
| 568 | {#header_open|Assignment#} | 568 | {#header_open|Assignment#} |
| 569 | <p>Use <code>const</code> to assign a value to an identifier:</p> | 569 | <p>Use the <code>const</code> keyword to assign a value to an identifier:</p> |
| 570 | {#code_begin|test_err|cannot assign to constant#} | 570 | {#code_begin|test_err|cannot assign to constant#} |
| 571 | const x = 1234; | 571 | const x = 1234; |
| 572 | 572 | ||
| ... | @@ -582,7 +582,8 @@ test "assignment" { | ... | @@ -582,7 +582,8 @@ test "assignment" { |
| 582 | foo(); | 582 | foo(); |
| 583 | } | 583 | } |
| 584 | {#code_end#} | 584 | {#code_end#} |
| 585 | <p>If you need a variable that you can modify, use <code>var</code>:</p> | 585 | <p><code>const</code> applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p> |
| 586 | <p>If you need a variable that you can modify, use the <code>var</code> keyword:</p> | ||
| 586 | {#code_begin|test#} | 587 | {#code_begin|test#} |
| 587 | const assert = @import("std").debug.assert; | 588 | const assert = @import("std").debug.assert; |
| 588 | 589 | ||
| ... | @@ -1918,6 +1919,32 @@ test "linked list" { | ... | @@ -1918,6 +1919,32 @@ test "linked list" { |
| 1918 | assert(list2.first.?.data == 1234); | 1919 | assert(list2.first.?.data == 1234); |
| 1919 | } | 1920 | } |
| 1920 | {#code_end#} | 1921 | {#code_end#} |
| 1922 | {#header_open|struct Naming#} | ||
| 1923 | <p>Since all structs are anonymous, Zig infers the type name based on a few rules.</p> | ||
| 1924 | <ul> | ||
| 1925 | <li>If the struct is in the initialization expression of a variable, it gets named after | ||
| 1926 | that variable.</li> | ||
| 1927 | <li>If the struct is in the <code>return</code> expression, it gets named after | ||
| 1928 | the function it is returning from, with the parameter values serialized.</li> | ||
| 1929 | <li>Otherwise, the struct gets a same such as <code>(anonymous struct at file.zig:7:38)</code>.</li> | ||
| 1930 | </ul> | ||
| 1931 | {#code_begin|exe|struct_name#} | ||
| 1932 | const std = @import("std"); | ||
| 1933 | |||
| 1934 | pub fn main() void { | ||
| 1935 | const Foo = struct {}; | ||
| 1936 | std.debug.warn("variable: {}\n", @typeName(Foo)); | ||
| 1937 | std.debug.warn("anonymous: {}\n", @typeName(struct {})); | ||
| 1938 | std.debug.warn("function: {}\n", @typeName(List(i32))); | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | fn List(comptime T: type) type { | ||
| 1942 | return struct { | ||
| 1943 | x: T, | ||
| 1944 | }; | ||
| 1945 | } | ||
| 1946 | {#code_end#} | ||
| 1947 | {#header_close#} | ||
| 1921 | {#see_also|comptime|@fieldParentPtr#} | 1948 | {#see_also|comptime|@fieldParentPtr#} |
| 1922 | {#header_close#} | 1949 | {#header_close#} |
| 1923 | {#header_open|enum#} | 1950 | {#header_open|enum#} |
| ... | @@ -2179,6 +2206,39 @@ test "@tagName" { | ... | @@ -2179,6 +2206,39 @@ test "@tagName" { |
| 2179 | sorts the order of the tag and union field by the largest alignment. | 2206 | sorts the order of the tag and union field by the largest alignment. |
| 2180 | </p> | 2207 | </p> |
| 2181 | {#header_close#} | 2208 | {#header_close#} |
| 2209 | {#header_open|blocks#} | ||
| 2210 | <p> | ||
| 2211 | Blocks are used to limit the scope of variable declarations: | ||
| 2212 | </p> | ||
| 2213 | {#code_begin|test_err|undeclared identifier#} | ||
| 2214 | test "access variable after block scope" { | ||
| 2215 | { | ||
| 2216 | var x: i32 = 1; | ||
| 2217 | } | ||
| 2218 | x += 1; | ||
| 2219 | } | ||
| 2220 | {#code_end#} | ||
| 2221 | <p>Blocks are expressions. When labeled, <code>break</code> can be used | ||
| 2222 | to return a value from the block: | ||
| 2223 | </p> | ||
| 2224 | {#code_begin|test#} | ||
| 2225 | const std = @import("std"); | ||
| 2226 | const assert = std.debug.assert; | ||
| 2227 | |||
| 2228 | test "labeled break from labeled block expression" { | ||
| 2229 | var y: i32 = 123; | ||
| 2230 | |||
| 2231 | const x = blk: { | ||
| 2232 | y += 1; | ||
| 2233 | break :blk y; | ||
| 2234 | }; | ||
| 2235 | assert(x == 124); | ||
| 2236 | assert(y == 124); | ||
| 2237 | } | ||
| 2238 | {#code_end#} | ||
| 2239 | <p>Here, <code>blk</code> can be any name.</p> | ||
| 2240 | {#see_also|Labeled while|Labeled for#} | ||
| 2241 | {#header_close#} | ||
| 2182 | {#header_open|switch#} | 2242 | {#header_open|switch#} |
| 2183 | {#code_begin|test|switch#} | 2243 | {#code_begin|test|switch#} |
| 2184 | const assert = @import("std").debug.assert; | 2244 | const assert = @import("std").debug.assert; |
| ... | @@ -2374,6 +2434,28 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool { | ... | @@ -2374,6 +2434,28 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool { |
| 2374 | } else false; | 2434 | } else false; |
| 2375 | } | 2435 | } |
| 2376 | {#code_end#} | 2436 | {#code_end#} |
| 2437 | {#header_open|Labeled while#} | ||
| 2438 | <p>When a <code>while</code> loop is labeled, it can be referenced from a <code>break</code> | ||
| 2439 | or <code>continue</code> from within a nested loop:</p> | ||
| 2440 | {#code_begin|test#} | ||
| 2441 | test "nested break" { | ||
| 2442 | outer: while (true) { | ||
| 2443 | while (true) { | ||
| 2444 | break :outer; | ||
| 2445 | } | ||
| 2446 | } | ||
| 2447 | } | ||
| 2448 | |||
| 2449 | test "nested continue" { | ||
| 2450 | var i: usize = 0; | ||
| 2451 | outer: while (i < 10) : (i += 1) { | ||
| 2452 | while (true) { | ||
| 2453 | continue :outer; | ||
| 2454 | } | ||
| 2455 | } | ||
| 2456 | } | ||
| 2457 | {#code_end#} | ||
| 2458 | {#header_close#} | ||
| 2377 | {#header_open|while with Optionals#} | 2459 | {#header_open|while with Optionals#} |
| 2378 | <p> | 2460 | <p> |
| 2379 | Just like {#link|if#} expressions, while loops can take an optional as the | 2461 | Just like {#link|if#} expressions, while loops can take an optional as the |
| ... | @@ -2560,6 +2642,37 @@ test "for else" { | ... | @@ -2560,6 +2642,37 @@ test "for else" { |
| 2560 | }; | 2642 | }; |
| 2561 | } | 2643 | } |
| 2562 | {#code_end#} | 2644 | {#code_end#} |
| 2645 | {#header_open|Labeled for#} | ||
| 2646 | <p>When a <code>for</code> loop is labeled, it can be referenced from a <code>break</code> | ||
| 2647 | or <code>continue</code> from within a nested loop:</p> | ||
| 2648 | {#code_begin|test#} | ||
| 2649 | const std = @import("std"); | ||
| 2650 | const assert = std.debug.assert; | ||
| 2651 | |||
| 2652 | test "nested break" { | ||
| 2653 | var count: usize = 0; | ||
| 2654 | outer: for ([]i32{ 1, 2, 3, 4, 5 }) |_| { | ||
| 2655 | for ([]i32{ 1, 2, 3, 4, 5 }) |_| { | ||
| 2656 | count += 1; | ||
| 2657 | break :outer; | ||
| 2658 | } | ||
| 2659 | } | ||
| 2660 | assert(count == 1); | ||
| 2661 | } | ||
| 2662 | |||
| 2663 | test "nested continue" { | ||
| 2664 | var count: usize = 0; | ||
| 2665 | outer: for ([]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { | ||
| 2666 | for ([]i32{ 1, 2, 3, 4, 5 }) |_| { | ||
| 2667 | count += 1; | ||
| 2668 | continue :outer; | ||
| 2669 | } | ||
| 2670 | } | ||
| 2671 | |||
| 2672 | assert(count == 8); | ||
| 2673 | } | ||
| 2674 | {#code_end#} | ||
| 2675 | {#header_close#} | ||
| 2563 | {#header_open|inline for#} | 2676 | {#header_open|inline for#} |
| 2564 | <p> | 2677 | <p> |
| 2565 | For loops can be inlined. This causes the loop to be unrolled, which | 2678 | For loops can be inlined. This causes the loop to be unrolled, which |
| ... | @@ -7057,6 +7170,61 @@ const c = @cImport({ | ... | @@ -7057,6 +7170,61 @@ const c = @cImport({ |
| 7057 | }); | 7170 | }); |
| 7058 | {#code_end#} | 7171 | {#code_end#} |
| 7059 | {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} | 7172 | {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} |
| 7173 | {#header_close#} | ||
| 7174 | {#header_open|Exporting a C Library#} | ||
| 7175 | <p> | ||
| 7176 | One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages | ||
| 7177 | to call into. The <code>export</code> keyword in front of functions, variables, and types causes them to | ||
| 7178 | be part of the library API: | ||
| 7179 | </p> | ||
| 7180 | <p class="file">mathtest.zig</p> | ||
| 7181 | {#code_begin|syntax#} | ||
| 7182 | export fn add(a: i32, b: i32) i32 { | ||
| 7183 | return a + b; | ||
| 7184 | } | ||
| 7185 | {#code_end#} | ||
| 7186 | <p>To make a shared library:</p> | ||
| 7187 | <pre><code class="shell">$ zig build-lib mathtest.zig | ||
| 7188 | </code></pre> | ||
| 7189 | <p>To make a static library:</p> | ||
| 7190 | <pre><code class="shell">$ zig build-lib mathtest.zig --static | ||
| 7191 | </code></pre> | ||
| 7192 | <p>Here is an example with the {#link|Zig Build System#}:</p> | ||
| 7193 | <p class="file">test.c</p> | ||
| 7194 | <pre><code class="cpp">// This header is generated by zig from mathtest.zig | ||
| 7195 | #include "mathtest.h" | ||
| 7196 | #include &lt;assert.h&gt; | ||
| 7197 | |||
| 7198 | int main(int argc, char **argv) { | ||
| 7199 | assert(add(42, 1337) == 1379); | ||
| 7200 | return 0; | ||
| 7201 | }</code></pre> | ||
| 7202 | <p class="file">build.zig</p> | ||
| 7203 | {#code_begin|syntax#} | ||
| 7204 | const Builder = @import("std").build.Builder; | ||
| 7205 | |||
| 7206 | pub fn build(b: *Builder) void { | ||
| 7207 | const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); | ||
| 7208 | |||
| 7209 | const exe = b.addCExecutable("test"); | ||
| 7210 | exe.addCompileFlags([][]const u8{"-std=c99"}); | ||
| 7211 | exe.addSourceFile("test.c"); | ||
| 7212 | exe.linkLibrary(lib); | ||
| 7213 | |||
| 7214 | b.default_step.dependOn(&exe.step); | ||
| 7215 | |||
| 7216 | const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()}); | ||
| 7217 | run_cmd.step.dependOn(&exe.step); | ||
| 7218 | |||
| 7219 | const test_step = b.step("test", "Test the program"); | ||
| 7220 | test_step.dependOn(&run_cmd.step); | ||
| 7221 | } | ||
| 7222 | {#code_end#} | ||
| 7223 | <p class="file">terminal</p> | ||
| 7224 | <pre><code class="shell">$ zig build | ||
| 7225 | $ ./test | ||
| 7226 | $ echo $? | ||
| 7227 | 0</code></pre> | ||
| 7060 | {#header_close#} | 7228 | {#header_close#} |
| 7061 | {#header_open|Mixing Object Files#} | 7229 | {#header_open|Mixing Object Files#} |
| 7062 | <p> | 7230 | <p> |
example/guess_number/main.zig+1-1| ... | @@ -6,7 +6,7 @@ const os = std.os; | ... | @@ -6,7 +6,7 @@ const os = std.os; |
| 6 | 6 | ||
| 7 | pub fn main() !void { | 7 | pub fn main() !void { |
| 8 | var stdout_file = try io.getStdOut(); | 8 | var stdout_file = try io.getStdOut(); |
| 9 | var stdout_file_stream = io.FileOutStream.init(&stdout_file); | 9 | var stdout_file_stream = io.FileOutStream.init(stdout_file); |
| 10 | const stdout = &stdout_file_stream.stream; | 10 | const stdout = &stdout_file_stream.stream; |
| 11 | 11 | ||
| 12 | try stdout.print("Welcome to the Guess Number Game in Zig.\n"); | 12 | try stdout.print("Welcome to the Guess Number Game in Zig.\n"); |
src-self-hosted/errmsg.zig+1-1| ... | @@ -272,7 +272,7 @@ pub const Msg = struct { | ... | @@ -272,7 +272,7 @@ pub const Msg = struct { |
| 272 | try stream.write("\n"); | 272 | try stream.write("\n"); |
| 273 | } | 273 | } |
| 274 | 274 | ||
| 275 | pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void { | 275 | pub fn printToFile(msg: *const Msg, file: os.File, color: Color) !void { |
| 276 | const color_on = switch (color) { | 276 | const color_on = switch (color) { |
| 277 | Color.Auto => file.isTty(), | 277 | Color.Auto => file.isTty(), |
| 278 | Color.On => true, | 278 | Color.On => true, |
src-self-hosted/main.zig+6-6| ... | @@ -55,11 +55,11 @@ pub fn main() !void { | ... | @@ -55,11 +55,11 @@ pub fn main() !void { |
| 55 | const allocator = std.heap.c_allocator; | 55 | const allocator = std.heap.c_allocator; |
| 56 | 56 | ||
| 57 | var stdout_file = try std.io.getStdOut(); | 57 | var stdout_file = try std.io.getStdOut(); |
| 58 | var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); | 58 | var stdout_out_stream = std.io.FileOutStream.init(stdout_file); |
| 59 | stdout = &stdout_out_stream.stream; | 59 | stdout = &stdout_out_stream.stream; |
| 60 | 60 | ||
| 61 | stderr_file = try std.io.getStdErr(); | 61 | stderr_file = try std.io.getStdErr(); |
| 62 | var stderr_out_stream = std.io.FileOutStream.init(&stderr_file); | 62 | var stderr_out_stream = std.io.FileOutStream.init(stderr_file); |
| 63 | stderr = &stderr_out_stream.stream; | 63 | stderr = &stderr_out_stream.stream; |
| 64 | 64 | ||
| 65 | const args = try os.argsAlloc(allocator); | 65 | const args = try os.argsAlloc(allocator); |
| ... | @@ -491,7 +491,7 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { | ... | @@ -491,7 +491,7 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { |
| 491 | stderr.print("Build {} compile errors:\n", count) catch os.exit(1); | 491 | stderr.print("Build {} compile errors:\n", count) catch os.exit(1); |
| 492 | for (msgs) |msg| { | 492 | for (msgs) |msg| { |
| 493 | defer msg.destroy(); | 493 | defer msg.destroy(); |
| 494 | msg.printToFile(&stderr_file, color) catch os.exit(1); | 494 | msg.printToFile(stderr_file, color) catch os.exit(1); |
| 495 | } | 495 | } |
| 496 | }, | 496 | }, |
| 497 | } | 497 | } |
| ... | @@ -619,7 +619,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { | ... | @@ -619,7 +619,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { |
| 619 | } | 619 | } |
| 620 | 620 | ||
| 621 | var stdin_file = try io.getStdIn(); | 621 | var stdin_file = try io.getStdIn(); |
| 622 | var stdin = io.FileInStream.init(&stdin_file); | 622 | var stdin = io.FileInStream.init(stdin_file); |
| 623 | 623 | ||
| 624 | const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size); | 624 | const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size); |
| 625 | defer allocator.free(source_code); | 625 | defer allocator.free(source_code); |
| ... | @@ -635,7 +635,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { | ... | @@ -635,7 +635,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { |
| 635 | const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>"); | 635 | const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>"); |
| 636 | defer msg.destroy(); | 636 | defer msg.destroy(); |
| 637 | 637 | ||
| 638 | try msg.printToFile(&stderr_file, color); | 638 | try msg.printToFile(stderr_file, color); |
| 639 | } | 639 | } |
| 640 | if (tree.errors.len != 0) { | 640 | if (tree.errors.len != 0) { |
| 641 | os.exit(1); | 641 | os.exit(1); |
| ... | @@ -772,7 +772,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { | ... | @@ -772,7 +772,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void { |
| 772 | const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path); | 772 | const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path); |
| 773 | defer fmt.loop.allocator.destroy(msg); | 773 | defer fmt.loop.allocator.destroy(msg); |
| 774 | 774 | ||
| 775 | try msg.printToFile(&stderr_file, fmt.color); | 775 | try msg.printToFile(stderr_file, fmt.color); |
| 776 | } | 776 | } |
| 777 | if (tree.errors.len != 0) { | 777 | if (tree.errors.len != 0) { |
| 778 | fmt.any_error = true; | 778 | fmt.any_error = true; |
src-self-hosted/test.zig+2-2| ... | @@ -185,7 +185,7 @@ pub const TestContext = struct { | ... | @@ -185,7 +185,7 @@ pub const TestContext = struct { |
| 185 | try stderr.write("build incorrectly failed:\n"); | 185 | try stderr.write("build incorrectly failed:\n"); |
| 186 | for (msgs) |msg| { | 186 | for (msgs) |msg| { |
| 187 | defer msg.destroy(); | 187 | defer msg.destroy(); |
| 188 | try msg.printToFile(&stderr, errmsg.Color.Auto); | 188 | try msg.printToFile(stderr, errmsg.Color.Auto); |
| 189 | } | 189 | } |
| 190 | }, | 190 | }, |
| 191 | } | 191 | } |
| ... | @@ -234,7 +234,7 @@ pub const TestContext = struct { | ... | @@ -234,7 +234,7 @@ pub const TestContext = struct { |
| 234 | var stderr = try std.io.getStdErr(); | 234 | var stderr = try std.io.getStdErr(); |
| 235 | for (msgs) |msg| { | 235 | for (msgs) |msg| { |
| 236 | defer msg.destroy(); | 236 | defer msg.destroy(); |
| 237 | try msg.printToFile(&stderr, errmsg.Color.Auto); | 237 | try msg.printToFile(stderr, errmsg.Color.Auto); |
| 238 | } | 238 | } |
| 239 | std.debug.warn("============\n"); | 239 | std.debug.warn("============\n"); |
| 240 | return error.TestFailed; | 240 | return error.TestFailed; |
src/all_types.hpp+6| ... | @@ -43,6 +43,7 @@ struct IrAnalyze; | ... | @@ -43,6 +43,7 @@ struct IrAnalyze; |
| 43 | struct IrExecutable { | 43 | struct IrExecutable { |
| 44 | ZigList<IrBasicBlock *> basic_block_list; | 44 | ZigList<IrBasicBlock *> basic_block_list; |
| 45 | Buf *name; | 45 | Buf *name; |
| 46 | FnTableEntry *name_fn; | ||
| 46 | size_t mem_slot_count; | 47 | size_t mem_slot_count; |
| 47 | size_t next_debug_id; | 48 | size_t next_debug_id; |
| 48 | size_t *backward_branch_count; | 49 | size_t *backward_branch_count; |
| ... | @@ -1805,6 +1806,11 @@ struct VariableTableEntry { | ... | @@ -1805,6 +1806,11 @@ struct VariableTableEntry { |
| 1805 | VarLinkage linkage; | 1806 | VarLinkage linkage; |
| 1806 | IrInstruction *decl_instruction; | 1807 | IrInstruction *decl_instruction; |
| 1807 | uint32_t align_bytes; | 1808 | uint32_t align_bytes; |
| 1809 | |||
| 1810 | // In an inline loop, multiple variables may be created, | ||
| 1811 | // In this case, a reference to a variable should follow | ||
| 1812 | // this pointer to the redefined variable. | ||
| 1813 | VariableTableEntry *next_var; | ||
| 1808 | }; | 1814 | }; |
| 1809 | 1815 | ||
| 1810 | struct ErrorTableEntry { | 1816 | struct ErrorTableEntry { |
src/analyze.cpp+13-11| ... | @@ -1575,7 +1575,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c | ... | @@ -1575,7 +1575,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1575 | 1575 | ||
| 1576 | switch (type_entry->id) { | 1576 | switch (type_entry->id) { |
| 1577 | case TypeTableEntryIdInvalid: | 1577 | case TypeTableEntryIdInvalid: |
| 1578 | return g->builtin_types.entry_invalid; | 1578 | zig_unreachable(); |
| 1579 | case TypeTableEntryIdUnreachable: | 1579 | case TypeTableEntryIdUnreachable: |
| 1580 | case TypeTableEntryIdUndefined: | 1580 | case TypeTableEntryIdUndefined: |
| 1581 | case TypeTableEntryIdNull: | 1581 | case TypeTableEntryIdNull: |
| ... | @@ -1680,14 +1680,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c | ... | @@ -1680,14 +1680,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1680 | case TypeTableEntryIdBlock: | 1680 | case TypeTableEntryIdBlock: |
| 1681 | case TypeTableEntryIdBoundFn: | 1681 | case TypeTableEntryIdBoundFn: |
| 1682 | case TypeTableEntryIdMetaType: | 1682 | case TypeTableEntryIdMetaType: |
| 1683 | if (!calling_convention_allows_zig_types(fn_type_id.cc)) { | ||
| 1684 | add_node_error(g, fn_proto->return_type, | ||
| 1685 | buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", | ||
| 1686 | buf_ptr(&fn_type_id.return_type->name), | ||
| 1687 | calling_convention_name(fn_type_id.cc))); | ||
| 1688 | return g->builtin_types.entry_invalid; | ||
| 1689 | } | ||
| 1690 | return get_generic_fn_type(g, &fn_type_id); | ||
| 1691 | case TypeTableEntryIdUnreachable: | 1683 | case TypeTableEntryIdUnreachable: |
| 1692 | case TypeTableEntryIdVoid: | 1684 | case TypeTableEntryIdVoid: |
| 1693 | case TypeTableEntryIdBool: | 1685 | case TypeTableEntryIdBool: |
| ... | @@ -1703,6 +1695,11 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c | ... | @@ -1703,6 +1695,11 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1703 | case TypeTableEntryIdUnion: | 1695 | case TypeTableEntryIdUnion: |
| 1704 | case TypeTableEntryIdFn: | 1696 | case TypeTableEntryIdFn: |
| 1705 | case TypeTableEntryIdPromise: | 1697 | case TypeTableEntryIdPromise: |
| 1698 | if ((err = type_ensure_zero_bits_known(g, fn_type_id.return_type))) | ||
| 1699 | return g->builtin_types.entry_invalid; | ||
| 1700 | if (type_requires_comptime(fn_type_id.return_type)) { | ||
| 1701 | return get_generic_fn_type(g, &fn_type_id); | ||
| 1702 | } | ||
| 1706 | break; | 1703 | break; |
| 1707 | } | 1704 | } |
| 1708 | 1705 | ||
| ... | @@ -3245,6 +3242,13 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { | ... | @@ -3245,6 +3242,13 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) { |
| 3245 | } else if (tld->id == TldIdFn) { | 3242 | } else if (tld->id == TldIdFn) { |
| 3246 | assert(tld->source_node->type == NodeTypeFnProto); | 3243 | assert(tld->source_node->type == NodeTypeFnProto); |
| 3247 | is_export = tld->source_node->data.fn_proto.is_export; | 3244 | is_export = tld->source_node->data.fn_proto.is_export; |
| 3245 | |||
| 3246 | if (!is_export && !tld->source_node->data.fn_proto.is_extern && | ||
| 3247 | tld->source_node->data.fn_proto.fn_def_node == nullptr) | ||
| 3248 | { | ||
| 3249 | add_node_error(g, tld->source_node, buf_sprintf("non-extern function has no body")); | ||
| 3250 | return; | ||
| 3251 | } | ||
| 3248 | } | 3252 | } |
| 3249 | if (is_export) { | 3253 | if (is_export) { |
| 3250 | g->resolve_queue.append(tld); | 3254 | g->resolve_queue.append(tld); |
| ... | @@ -5620,8 +5624,6 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue * | ... | @@ -5620,8 +5624,6 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue * |
| 5620 | if (type_entry->id == TypeTableEntryIdInt) { | 5624 | if (type_entry->id == TypeTableEntryIdInt) { |
| 5621 | const_val->special = ConstValSpecialStatic; | 5625 | const_val->special = ConstValSpecialStatic; |
| 5622 | eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max); | 5626 | eval_min_max_value_int(g, type_entry, &const_val->data.x_bigint, is_max); |
| 5623 | } else if (type_entry->id == TypeTableEntryIdFloat) { | ||
| 5624 | zig_panic("TODO analyze_min_max_value float"); | ||
| 5625 | } else if (type_entry->id == TypeTableEntryIdBool) { | 5627 | } else if (type_entry->id == TypeTableEntryIdBool) { |
| 5626 | const_val->special = ConstValSpecialStatic; | 5628 | const_val->special = ConstValSpecialStatic; |
| 5627 | const_val->data.x_bool = is_max; | 5629 | const_val->data.x_bool = is_max; |
src/codegen.cpp+12-1| ... | @@ -2618,6 +2618,9 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, | ... | @@ -2618,6 +2618,9 @@ static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, |
| 2618 | IrInstructionPtrCast *instruction) | 2618 | IrInstructionPtrCast *instruction) |
| 2619 | { | 2619 | { |
| 2620 | TypeTableEntry *wanted_type = instruction->base.value.type; | 2620 | TypeTableEntry *wanted_type = instruction->base.value.type; |
| 2621 | if (!type_has_bits(wanted_type)) { | ||
| 2622 | return nullptr; | ||
| 2623 | } | ||
| 2621 | LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); | 2624 | LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); |
| 2622 | return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); | 2625 | return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); |
| 2623 | } | 2626 | } |
| ... | @@ -3036,6 +3039,12 @@ static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { | ... | @@ -3036,6 +3039,12 @@ static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { |
| 3036 | LLVMBuildCall(g->builder, write_register_fn_val, params, 2, ""); | 3039 | LLVMBuildCall(g->builder, write_register_fn_val, params, 2, ""); |
| 3037 | } | 3040 | } |
| 3038 | 3041 | ||
| 3042 | static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { | ||
| 3043 | unsigned attr_kind_id = LLVMGetEnumAttributeKindForName("sret", 4); | ||
| 3044 | LLVMAttributeRef sret_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), attr_kind_id, 1); | ||
| 3045 | LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); | ||
| 3046 | } | ||
| 3047 | |||
| 3039 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { | 3048 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { |
| 3040 | LLVMValueRef fn_val; | 3049 | LLVMValueRef fn_val; |
| 3041 | TypeTableEntry *fn_type; | 3050 | TypeTableEntry *fn_type; |
| ... | @@ -3131,6 +3140,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr | ... | @@ -3131,6 +3140,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3131 | } else if (!ret_has_bits) { | 3140 | } else if (!ret_has_bits) { |
| 3132 | return nullptr; | 3141 | return nullptr; |
| 3133 | } else if (first_arg_ret) { | 3142 | } else if (first_arg_ret) { |
| 3143 | set_call_instr_sret(g, result); | ||
| 3134 | return instruction->tmp_ptr; | 3144 | return instruction->tmp_ptr; |
| 3135 | } else if (handle_is_ptr(src_return_type)) { | 3145 | } else if (handle_is_ptr(src_return_type)) { |
| 3136 | auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr); | 3146 | auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr); |
| ... | @@ -4573,8 +4583,9 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f | ... | @@ -4573,8 +4583,9 @@ static LLVMValueRef get_coro_alloc_helper_fn_val(CodeGen *g, LLVMTypeRef alloc_f |
| 4573 | args.append(allocator_val); | 4583 | args.append(allocator_val); |
| 4574 | args.append(coro_size); | 4584 | args.append(coro_size); |
| 4575 | args.append(alignment_val); | 4585 | args.append(alignment_val); |
| 4576 | ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length, | 4586 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, alloc_fn_val, args.items, args.length, |
| 4577 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); | 4587 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); |
| 4588 | set_call_instr_sret(g, call_instruction); | ||
| 4578 | LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, ""); | 4589 | LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, sret_ptr, err_union_err_index, ""); |
| 4579 | LLVMValueRef err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); | 4590 | LLVMValueRef err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); |
| 4580 | LLVMBuildStore(g->builder, err_val, err_code_ptr); | 4591 | LLVMBuildStore(g->builder, err_val, err_code_ptr); |
src/ir.cpp+173-135| ... | @@ -17,8 +17,7 @@ | ... | @@ -17,8 +17,7 @@ |
| 17 | #include "util.hpp" | 17 | #include "util.hpp" |
| 18 | 18 | ||
| 19 | struct IrExecContext { | 19 | struct IrExecContext { |
| 20 | ConstExprValue *mem_slot_list; | 20 | ZigList<ConstExprValue *> mem_slot_list; |
| 21 | size_t mem_slot_count; | ||
| 22 | }; | 21 | }; |
| 23 | 22 | ||
| 24 | struct IrBuilder { | 23 | struct IrBuilder { |
| ... | @@ -60,7 +59,7 @@ enum ConstCastResultId { | ... | @@ -60,7 +59,7 @@ enum ConstCastResultId { |
| 60 | ConstCastResultIdType, | 59 | ConstCastResultIdType, |
| 61 | ConstCastResultIdUnresolvedInferredErrSet, | 60 | ConstCastResultIdUnresolvedInferredErrSet, |
| 62 | ConstCastResultIdAsyncAllocatorType, | 61 | ConstCastResultIdAsyncAllocatorType, |
| 63 | ConstCastResultIdNullWrapPtr, | 62 | ConstCastResultIdNullWrapPtr |
| 64 | }; | 63 | }; |
| 65 | 64 | ||
| 66 | struct ConstCastOnly; | 65 | struct ConstCastOnly; |
| ... | @@ -155,18 +154,22 @@ static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type | ... | @@ -155,18 +154,22 @@ static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type |
| 155 | ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { | 154 | ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { |
| 156 | assert(get_codegen_ptr_type(const_val->type) != nullptr); | 155 | assert(get_codegen_ptr_type(const_val->type) != nullptr); |
| 157 | assert(const_val->special == ConstValSpecialStatic); | 156 | assert(const_val->special == ConstValSpecialStatic); |
| 157 | ConstExprValue *result; | ||
| 158 | switch (const_val->data.x_ptr.special) { | 158 | switch (const_val->data.x_ptr.special) { |
| 159 | case ConstPtrSpecialInvalid: | 159 | case ConstPtrSpecialInvalid: |
| 160 | zig_unreachable(); | 160 | zig_unreachable(); |
| 161 | case ConstPtrSpecialRef: | 161 | case ConstPtrSpecialRef: |
| 162 | return const_val->data.x_ptr.data.ref.pointee; | 162 | result = const_val->data.x_ptr.data.ref.pointee; |
| 163 | break; | ||
| 163 | case ConstPtrSpecialBaseArray: | 164 | case ConstPtrSpecialBaseArray: |
| 164 | expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); | 165 | expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); |
| 165 | return &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[ | 166 | result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.s_none.elements[ |
| 166 | const_val->data.x_ptr.data.base_array.elem_index]; | 167 | const_val->data.x_ptr.data.base_array.elem_index]; |
| 168 | break; | ||
| 167 | case ConstPtrSpecialBaseStruct: | 169 | case ConstPtrSpecialBaseStruct: |
| 168 | return &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ | 170 | result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ |
| 169 | const_val->data.x_ptr.data.base_struct.field_index]; | 171 | const_val->data.x_ptr.data.base_struct.field_index]; |
| 172 | break; | ||
| 170 | case ConstPtrSpecialHardCodedAddr: | 173 | case ConstPtrSpecialHardCodedAddr: |
| 171 | zig_unreachable(); | 174 | zig_unreachable(); |
| 172 | case ConstPtrSpecialDiscard: | 175 | case ConstPtrSpecialDiscard: |
| ... | @@ -174,7 +177,8 @@ ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { | ... | @@ -174,7 +177,8 @@ ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { |
| 174 | case ConstPtrSpecialFunction: | 177 | case ConstPtrSpecialFunction: |
| 175 | zig_unreachable(); | 178 | zig_unreachable(); |
| 176 | } | 179 | } |
| 177 | zig_unreachable(); | 180 | assert(result != nullptr); |
| 181 | return result; | ||
| 178 | } | 182 | } |
| 179 | 183 | ||
| 180 | static bool ir_should_inline(IrExecutable *exec, Scope *scope) { | 184 | static bool ir_should_inline(IrExecutable *exec, Scope *scope) { |
| ... | @@ -3181,7 +3185,11 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -3181,7 +3185,11 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 3181 | { | 3185 | { |
| 3182 | IrInstruction *return_value; | 3186 | IrInstruction *return_value; |
| 3183 | if (expr_node) { | 3187 | if (expr_node) { |
| 3188 | // Temporarily set this so that if we return a type it gets the name of the function | ||
| 3189 | FnTableEntry *prev_name_fn = irb->exec->name_fn; | ||
| 3190 | irb->exec->name_fn = exec_fn_entry(irb->exec); | ||
| 3184 | return_value = ir_gen_node(irb, expr_node, scope); | 3191 | return_value = ir_gen_node(irb, expr_node, scope); |
| 3192 | irb->exec->name_fn = prev_name_fn; | ||
| 3185 | if (return_value == irb->codegen->invalid_instruction) | 3193 | if (return_value == irb->codegen->invalid_instruction) |
| 3186 | return irb->codegen->invalid_instruction; | 3194 | return irb->codegen->invalid_instruction; |
| 3187 | } else { | 3195 | } else { |
| ... | @@ -3275,7 +3283,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, | ... | @@ -3275,7 +3283,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 3275 | } | 3283 | } |
| 3276 | 3284 | ||
| 3277 | static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, | 3285 | static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, |
| 3278 | Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) | 3286 | Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime, |
| 3287 | bool skip_name_check) | ||
| 3279 | { | 3288 | { |
| 3280 | VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1); | 3289 | VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1); |
| 3281 | variable_entry->parent_scope = parent_scope; | 3290 | variable_entry->parent_scope = parent_scope; |
| ... | @@ -3288,29 +3297,30 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco | ... | @@ -3288,29 +3297,30 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco |
| 3288 | if (name) { | 3297 | if (name) { |
| 3289 | buf_init_from_buf(&variable_entry->name, name); | 3298 | buf_init_from_buf(&variable_entry->name, name); |
| 3290 | 3299 | ||
| 3291 | VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name); | 3300 | if (!skip_name_check) { |
| 3292 | if (existing_var && !existing_var->shadowable) { | 3301 | VariableTableEntry *existing_var = find_variable(codegen, parent_scope, name); |
| 3293 | ErrorMsg *msg = add_node_error(codegen, node, | 3302 | if (existing_var && !existing_var->shadowable) { |
| 3294 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); | 3303 | ErrorMsg *msg = add_node_error(codegen, node, |
| 3295 | add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); | 3304 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); |
| 3296 | variable_entry->value->type = codegen->builtin_types.entry_invalid; | 3305 | add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); |
| 3297 | } else { | ||
| 3298 | TypeTableEntry *type = get_primitive_type(codegen, name); | ||
| 3299 | if (type != nullptr) { | ||
| 3300 | add_node_error(codegen, node, | ||
| 3301 | buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); | ||
| 3302 | variable_entry->value->type = codegen->builtin_types.entry_invalid; | 3306 | variable_entry->value->type = codegen->builtin_types.entry_invalid; |
| 3303 | } else { | 3307 | } else { |
| 3304 | Tld *tld = find_decl(codegen, parent_scope, name); | 3308 | TypeTableEntry *type = get_primitive_type(codegen, name); |
| 3305 | if (tld != nullptr) { | 3309 | if (type != nullptr) { |
| 3306 | ErrorMsg *msg = add_node_error(codegen, node, | 3310 | add_node_error(codegen, node, |
| 3307 | buf_sprintf("redefinition of '%s'", buf_ptr(name))); | 3311 | buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); |
| 3308 | add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); | ||
| 3309 | variable_entry->value->type = codegen->builtin_types.entry_invalid; | 3312 | variable_entry->value->type = codegen->builtin_types.entry_invalid; |
| 3313 | } else { | ||
| 3314 | Tld *tld = find_decl(codegen, parent_scope, name); | ||
| 3315 | if (tld != nullptr) { | ||
| 3316 | ErrorMsg *msg = add_node_error(codegen, node, | ||
| 3317 | buf_sprintf("redefinition of '%s'", buf_ptr(name))); | ||
| 3318 | add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); | ||
| 3319 | variable_entry->value->type = codegen->builtin_types.entry_invalid; | ||
| 3320 | } | ||
| 3310 | } | 3321 | } |
| 3311 | } | 3322 | } |
| 3312 | } | 3323 | } |
| 3313 | |||
| 3314 | } else { | 3324 | } else { |
| 3315 | assert(is_shadowable); | 3325 | assert(is_shadowable); |
| 3316 | // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" | 3326 | // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" |
| ... | @@ -3333,14 +3343,9 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s | ... | @@ -3333,14 +3343,9 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s |
| 3333 | bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) | 3343 | bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) |
| 3334 | { | 3344 | { |
| 3335 | bool is_underscored = name ? buf_eql_str(name, "_") : false; | 3345 | bool is_underscored = name ? buf_eql_str(name, "_") : false; |
| 3336 | VariableTableEntry *var = create_local_var( irb->codegen | 3346 | VariableTableEntry *var = create_local_var(irb->codegen, node, scope, |
| 3337 | , node | 3347 | (is_underscored ? nullptr : name), src_is_const, gen_is_const, |
| 3338 | , scope | 3348 | (is_underscored ? true : is_shadowable), is_comptime, false); |
| 3339 | , (is_underscored ? nullptr : name) | ||
| 3340 | , src_is_const | ||
| 3341 | , gen_is_const | ||
| 3342 | , (is_underscored ? true : is_shadowable) | ||
| 3343 | , is_comptime ); | ||
| 3344 | if (is_comptime != nullptr || gen_is_const) { | 3349 | if (is_comptime != nullptr || gen_is_const) { |
| 3345 | var->mem_slot_index = exec_next_mem_slot(irb->exec); | 3350 | var->mem_slot_index = exec_next_mem_slot(irb->exec); |
| 3346 | var->owner_exec = irb->exec; | 3351 | var->owner_exec = irb->exec; |
| ... | @@ -6479,20 +6484,17 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o | ... | @@ -6479,20 +6484,17 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o |
| 6479 | static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, AstNode *source_node) { | 6484 | static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, AstNode *source_node) { |
| 6480 | if (exec->name) { | 6485 | if (exec->name) { |
| 6481 | return exec->name; | 6486 | return exec->name; |
| 6487 | } else if (exec->name_fn != nullptr) { | ||
| 6488 | Buf *name = buf_alloc(); | ||
| 6489 | buf_append_buf(name, &exec->name_fn->symbol_name); | ||
| 6490 | buf_appendf(name, "("); | ||
| 6491 | render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope); | ||
| 6492 | buf_appendf(name, ")"); | ||
| 6493 | return name; | ||
| 6482 | } else { | 6494 | } else { |
| 6483 | FnTableEntry *fn_entry = exec_fn_entry(exec); | 6495 | //Note: C-imports do not have valid location information |
| 6484 | if (fn_entry) { | 6496 | return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name, |
| 6485 | Buf *name = buf_alloc(); | 6497 | (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1); |
| 6486 | buf_append_buf(name, &fn_entry->symbol_name); | ||
| 6487 | buf_appendf(name, "("); | ||
| 6488 | render_instance_name_recursive(codegen, name, &fn_entry->fndef_scope->base, exec->begin_scope); | ||
| 6489 | buf_appendf(name, ")"); | ||
| 6490 | return name; | ||
| 6491 | } else { | ||
| 6492 | //Note: C-imports do not have valid location information | ||
| 6493 | return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name, | ||
| 6494 | (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1); | ||
| 6495 | } | ||
| 6496 | } | 6498 | } |
| 6497 | } | 6499 | } |
| 6498 | 6500 | ||
| ... | @@ -6690,7 +6692,10 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo | ... | @@ -6690,7 +6692,10 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo |
| 6690 | return irb->codegen->invalid_instruction; | 6692 | return irb->codegen->invalid_instruction; |
| 6691 | } | 6693 | } |
| 6692 | } else { | 6694 | } else { |
| 6693 | return_type = nullptr; | 6695 | add_node_error(irb->codegen, node, |
| 6696 | buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); | ||
| 6697 | return irb->codegen->invalid_instruction; | ||
| 6698 | //return_type = nullptr; | ||
| 6694 | } | 6699 | } |
| 6695 | 6700 | ||
| 6696 | IrInstruction *async_allocator_type_value = nullptr; | 6701 | IrInstruction *async_allocator_type_value = nullptr; |
| ... | @@ -8466,9 +8471,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry | ... | @@ -8466,9 +8471,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry |
| 8466 | if (wanted_type == actual_type) | 8471 | if (wanted_type == actual_type) |
| 8467 | return result; | 8472 | return result; |
| 8468 | 8473 | ||
| 8469 | // * and [*] can do a const-cast-only to ?* and ?[*], respectively | 8474 | // *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively |
| 8470 | // but not if there is a mutable parent pointer | 8475 | // but not if we want a mutable pointer |
| 8471 | // and not if the pointer is zero bits | 8476 | // and not if the actual pointer has zero bits |
| 8472 | if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional && | 8477 | if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional && |
| 8473 | wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer && | 8478 | wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer && |
| 8474 | actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type)) | 8479 | actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type)) |
| ... | @@ -8483,6 +8488,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry | ... | @@ -8483,6 +8488,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry |
| 8483 | return result; | 8488 | return result; |
| 8484 | } | 8489 | } |
| 8485 | 8490 | ||
| 8491 | // *T and [*]T can always cast to *c_void | ||
| 8492 | if (wanted_type->id == TypeTableEntryIdPointer && | ||
| 8493 | wanted_type->data.pointer.ptr_len == PtrLenSingle && | ||
| 8494 | wanted_type->data.pointer.child_type == g->builtin_types.entry_c_void && | ||
| 8495 | actual_type->id == TypeTableEntryIdPointer && | ||
| 8496 | (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && | ||
| 8497 | (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) | ||
| 8498 | { | ||
| 8499 | assert(actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment); | ||
| 8500 | return result; | ||
| 8501 | } | ||
| 8502 | |||
| 8486 | // pointer const | 8503 | // pointer const |
| 8487 | if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) { | 8504 | if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) { |
| 8488 | ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, | 8505 | ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, |
| ... | @@ -12478,6 +12495,24 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc | ... | @@ -12478,6 +12495,24 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 12478 | } | 12495 | } |
| 12479 | } | 12496 | } |
| 12480 | 12497 | ||
| 12498 | if (var->value->type != nullptr && !is_comptime_var) { | ||
| 12499 | // This is at least the second time we've seen this variable declaration during analysis. | ||
| 12500 | // This means that this is actually a different variable due to, e.g. an inline while loop. | ||
| 12501 | // We make a new variable so that it can hold a different type, and so the debug info can | ||
| 12502 | // be distinct. | ||
| 12503 | VariableTableEntry *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope, | ||
| 12504 | &var->name, var->src_is_const, var->gen_is_const, var->shadowable, var->is_comptime, true); | ||
| 12505 | new_var->owner_exec = var->owner_exec; | ||
| 12506 | if (var->mem_slot_index != SIZE_MAX) { | ||
| 12507 | ConstExprValue *vals = create_const_vals(1); | ||
| 12508 | new_var->mem_slot_index = ira->exec_context.mem_slot_list.length; | ||
| 12509 | ira->exec_context.mem_slot_list.append(vals); | ||
| 12510 | } | ||
| 12511 | |||
| 12512 | var->next_var = new_var; | ||
| 12513 | var = new_var; | ||
| 12514 | } | ||
| 12515 | |||
| 12481 | var->value->type = result_type; | 12516 | var->value->type = result_type; |
| 12482 | assert(var->value->type); | 12517 | assert(var->value->type); |
| 12483 | 12518 | ||
| ... | @@ -12496,10 +12531,9 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc | ... | @@ -12496,10 +12531,9 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 12496 | 12531 | ||
| 12497 | if (casted_init_value->value.special != ConstValSpecialRuntime) { | 12532 | if (casted_init_value->value.special != ConstValSpecialRuntime) { |
| 12498 | if (var->mem_slot_index != SIZE_MAX) { | 12533 | if (var->mem_slot_index != SIZE_MAX) { |
| 12499 | assert(var->mem_slot_index < ira->exec_context.mem_slot_count); | 12534 | assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); |
| 12500 | ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; | 12535 | ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); |
| 12501 | copy_const_val(mem_slot, &casted_init_value->value, | 12536 | copy_const_val(mem_slot, &casted_init_value->value, !is_comptime_var || var->gen_is_const); |
| 12502 | !is_comptime_var || var->gen_is_const); | ||
| 12503 | 12537 | ||
| 12504 | if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { | 12538 | if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { |
| 12505 | ir_build_const_from(ira, &decl_var_instruction->base); | 12539 | ir_build_const_from(ira, &decl_var_instruction->base); |
| ... | @@ -12960,6 +12994,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, | ... | @@ -12960,6 +12994,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, |
| 12960 | VariableTableEntry *var) | 12994 | VariableTableEntry *var) |
| 12961 | { | 12995 | { |
| 12962 | Error err; | 12996 | Error err; |
| 12997 | while (var->next_var != nullptr) { | ||
| 12998 | var = var->next_var; | ||
| 12999 | } | ||
| 13000 | |||
| 12963 | if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) { | 13001 | if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) { |
| 12964 | assert(ira->codegen->errors.length != 0); | 13002 | assert(ira->codegen->errors.length != 0); |
| 12965 | return ira->codegen->invalid_instruction; | 13003 | return ira->codegen->invalid_instruction; |
| ... | @@ -12979,8 +13017,8 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, | ... | @@ -12979,8 +13017,8 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, |
| 12979 | assert(var->owner_exec != nullptr); | 13017 | assert(var->owner_exec != nullptr); |
| 12980 | assert(var->owner_exec->analysis != nullptr); | 13018 | assert(var->owner_exec->analysis != nullptr); |
| 12981 | IrExecContext *exec_context = &var->owner_exec->analysis->exec_context; | 13019 | IrExecContext *exec_context = &var->owner_exec->analysis->exec_context; |
| 12982 | assert(var->mem_slot_index < exec_context->mem_slot_count); | 13020 | assert(var->mem_slot_index < exec_context->mem_slot_list.length); |
| 12983 | mem_slot = &exec_context->mem_slot_list[var->mem_slot_index]; | 13021 | mem_slot = exec_context->mem_slot_list.at(var->mem_slot_index); |
| 12984 | } | 13022 | } |
| 12985 | } | 13023 | } |
| 12986 | 13024 | ||
| ... | @@ -14439,8 +14477,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ | ... | @@ -14439,8 +14477,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ |
| 14439 | ConstExprValue *payload_val = union_val->data.x_union.payload; | 14477 | ConstExprValue *payload_val = union_val->data.x_union.payload; |
| 14440 | 14478 | ||
| 14441 | TypeTableEntry *field_type = field->type_entry; | 14479 | TypeTableEntry *field_type = field->type_entry; |
| 14442 | if (field_type->id == TypeTableEntryIdVoid) | 14480 | if (field_type->id == TypeTableEntryIdVoid) { |
| 14443 | { | ||
| 14444 | assert(payload_val == nullptr); | 14481 | assert(payload_val == nullptr); |
| 14445 | payload_val = create_const_vals(1); | 14482 | payload_val = create_const_vals(1); |
| 14446 | payload_val->special = ConstValSpecialStatic; | 14483 | payload_val->special = ConstValSpecialStatic; |
| ... | @@ -16445,12 +16482,6 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ | ... | @@ -16445,12 +16482,6 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ |
| 16445 | eval_min_max_value(ira->codegen, target_type, out_val, is_max); | 16482 | eval_min_max_value(ira->codegen, target_type, out_val, is_max); |
| 16446 | return ira->codegen->builtin_types.entry_num_lit_int; | 16483 | return ira->codegen->builtin_types.entry_num_lit_int; |
| 16447 | } | 16484 | } |
| 16448 | case TypeTableEntryIdFloat: | ||
| 16449 | { | ||
| 16450 | ConstExprValue *out_val = ir_build_const_from(ira, source_instruction); | ||
| 16451 | eval_min_max_value(ira->codegen, target_type, out_val, is_max); | ||
| 16452 | return ira->codegen->builtin_types.entry_num_lit_float; | ||
| 16453 | } | ||
| 16454 | case TypeTableEntryIdBool: | 16485 | case TypeTableEntryIdBool: |
| 16455 | case TypeTableEntryIdVoid: | 16486 | case TypeTableEntryIdVoid: |
| 16456 | { | 16487 | { |
| ... | @@ -16459,7 +16490,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ | ... | @@ -16459,7 +16490,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ |
| 16459 | return target_type; | 16490 | return target_type; |
| 16460 | } | 16491 | } |
| 16461 | case TypeTableEntryIdEnum: | 16492 | case TypeTableEntryIdEnum: |
| 16462 | zig_panic("TODO min/max value for enum type"); | 16493 | case TypeTableEntryIdFloat: |
| 16463 | case TypeTableEntryIdMetaType: | 16494 | case TypeTableEntryIdMetaType: |
| 16464 | case TypeTableEntryIdUnreachable: | 16495 | case TypeTableEntryIdUnreachable: |
| 16465 | case TypeTableEntryIdPointer: | 16496 | case TypeTableEntryIdPointer: |
| ... | @@ -16792,12 +16823,11 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na | ... | @@ -16792,12 +16823,11 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na |
| 16792 | return var->value->data.x_type; | 16823 | return var->value->data.x_type; |
| 16793 | } | 16824 | } |
| 16794 | 16825 | ||
| 16795 | static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) | 16826 | static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { |
| 16796 | { | ||
| 16797 | Error err; | 16827 | Error err; |
| 16798 | TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr); | 16828 | TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr); |
| 16799 | if ((err = ensure_complete_type(ira->codegen, type_info_definition_type))) | 16829 | if ((err = ensure_complete_type(ira->codegen, type_info_definition_type))) |
| 16800 | return false; | 16830 | return err; |
| 16801 | 16831 | ||
| 16802 | ensure_field_index(type_info_definition_type, "name", 0); | 16832 | ensure_field_index(type_info_definition_type, "name", 0); |
| 16803 | ensure_field_index(type_info_definition_type, "is_pub", 1); | 16833 | ensure_field_index(type_info_definition_type, "is_pub", 1); |
| ... | @@ -16805,38 +16835,33 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop | ... | @@ -16805,38 +16835,33 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop |
| 16805 | 16835 | ||
| 16806 | TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); | 16836 | TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type); |
| 16807 | if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type))) | 16837 | if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type))) |
| 16808 | return false; | 16838 | return err; |
| 16809 | 16839 | ||
| 16810 | TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type); | 16840 | TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type); |
| 16811 | if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type))) | 16841 | if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type))) |
| 16812 | return false; | 16842 | return err; |
| 16813 | 16843 | ||
| 16814 | TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type); | 16844 | TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type); |
| 16815 | if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type))) | 16845 | if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type))) |
| 16816 | return false; | 16846 | return err; |
| 16817 | 16847 | ||
| 16818 | // Loop through our definitions once to figure out how many definitions we will generate info for. | 16848 | // Loop through our definitions once to figure out how many definitions we will generate info for. |
| 16819 | auto decl_it = decls_scope->decl_table.entry_iterator(); | 16849 | auto decl_it = decls_scope->decl_table.entry_iterator(); |
| 16820 | decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; | 16850 | decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; |
| 16821 | int definition_count = 0; | 16851 | int definition_count = 0; |
| 16822 | 16852 | ||
| 16823 | while ((curr_entry = decl_it.next()) != nullptr) | 16853 | while ((curr_entry = decl_it.next()) != nullptr) { |
| 16824 | { | ||
| 16825 | // If the definition is unresolved, force it to be resolved again. | 16854 | // If the definition is unresolved, force it to be resolved again. |
| 16826 | if (curr_entry->value->resolution == TldResolutionUnresolved) | 16855 | if (curr_entry->value->resolution == TldResolutionUnresolved) { |
| 16827 | { | ||
| 16828 | resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); | 16856 | resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); |
| 16829 | if (curr_entry->value->resolution != TldResolutionOk) | 16857 | if (curr_entry->value->resolution != TldResolutionOk) { |
| 16830 | { | 16858 | return ErrorSemanticAnalyzeFail; |
| 16831 | return false; | ||
| 16832 | } | 16859 | } |
| 16833 | } | 16860 | } |
| 16834 | 16861 | ||
| 16835 | // Skip comptime blocks and test functions. | 16862 | // Skip comptime blocks and test functions. |
| 16836 | if (curr_entry->value->id != TldIdCompTime) | 16863 | if (curr_entry->value->id != TldIdCompTime) { |
| 16837 | { | 16864 | if (curr_entry->value->id == TldIdFn) { |
| 16838 | if (curr_entry->value->id == TldIdFn) | ||
| 16839 | { | ||
| 16840 | FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; | 16865 | FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; |
| 16841 | if (fn_entry->is_test) | 16866 | if (fn_entry->is_test) |
| 16842 | continue; | 16867 | continue; |
| ... | @@ -16858,13 +16883,11 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop | ... | @@ -16858,13 +16883,11 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop |
| 16858 | decl_it = decls_scope->decl_table.entry_iterator(); | 16883 | decl_it = decls_scope->decl_table.entry_iterator(); |
| 16859 | curr_entry = nullptr; | 16884 | curr_entry = nullptr; |
| 16860 | int definition_index = 0; | 16885 | int definition_index = 0; |
| 16861 | while ((curr_entry = decl_it.next()) != nullptr) | 16886 | while ((curr_entry = decl_it.next()) != nullptr) { |
| 16862 | { | ||
| 16863 | // Skip comptime blocks and test functions. | 16887 | // Skip comptime blocks and test functions. |
| 16864 | if (curr_entry->value->id == TldIdCompTime) | 16888 | if (curr_entry->value->id == TldIdCompTime) { |
| 16865 | continue; | 16889 | continue; |
| 16866 | else if (curr_entry->value->id == TldIdFn) | 16890 | } else if (curr_entry->value->id == TldIdFn) { |
| 16867 | { | ||
| 16868 | FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; | 16891 | FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; |
| 16869 | if (fn_entry->is_test) | 16892 | if (fn_entry->is_test) |
| 16870 | continue; | 16893 | continue; |
| ... | @@ -16887,13 +16910,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop | ... | @@ -16887,13 +16910,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop |
| 16887 | inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; | 16910 | inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; |
| 16888 | inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; | 16911 | inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; |
| 16889 | 16912 | ||
| 16890 | switch (curr_entry->value->id) | 16913 | switch (curr_entry->value->id) { |
| 16891 | { | ||
| 16892 | case TldIdVar: | 16914 | case TldIdVar: |
| 16893 | { | 16915 | { |
| 16894 | VariableTableEntry *var = ((TldVar *)curr_entry->value)->var; | 16916 | VariableTableEntry *var = ((TldVar *)curr_entry->value)->var; |
| 16895 | if ((err = ensure_complete_type(ira->codegen, var->value->type))) | 16917 | if ((err = ensure_complete_type(ira->codegen, var->value->type))) |
| 16896 | return false; | 16918 | return ErrorSemanticAnalyzeFail; |
| 16897 | 16919 | ||
| 16898 | if (var->value->type->id == TypeTableEntryIdMetaType) | 16920 | if (var->value->type->id == TypeTableEntryIdMetaType) |
| 16899 | { | 16921 | { |
| ... | @@ -17024,7 +17046,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop | ... | @@ -17024,7 +17046,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop |
| 17024 | { | 17046 | { |
| 17025 | TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry; | 17047 | TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry; |
| 17026 | if ((err = ensure_complete_type(ira->codegen, type_entry))) | 17048 | if ((err = ensure_complete_type(ira->codegen, type_entry))) |
| 17027 | return false; | 17049 | return ErrorSemanticAnalyzeFail; |
| 17028 | 17050 | ||
| 17029 | // This is a type. | 17051 | // This is a type. |
| 17030 | bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); | 17052 | bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); |
| ... | @@ -17046,7 +17068,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop | ... | @@ -17046,7 +17068,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop |
| 17046 | } | 17068 | } |
| 17047 | 17069 | ||
| 17048 | assert(definition_index == definition_count); | 17070 | assert(definition_index == definition_count); |
| 17049 | return true; | 17071 | return ErrorNone; |
| 17050 | } | 17072 | } |
| 17051 | 17073 | ||
| 17052 | static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry *ptr_type_entry) { | 17074 | static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry *ptr_type_entry) { |
| ... | @@ -17104,30 +17126,31 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry | ... | @@ -17104,30 +17126,31 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry |
| 17104 | return result; | 17126 | return result; |
| 17105 | }; | 17127 | }; |
| 17106 | 17128 | ||
| 17107 | static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) { | 17129 | static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, TypeEnumField *enum_field, |
| 17108 | Error err; | 17130 | TypeTableEntry *type_info_enum_field_type) |
| 17109 | assert(type_entry != nullptr); | 17131 | { |
| 17110 | assert(!type_is_invalid(type_entry)); | 17132 | enum_field_val->special = ConstValSpecialStatic; |
| 17133 | enum_field_val->type = type_info_enum_field_type; | ||
| 17111 | 17134 | ||
| 17112 | if ((err = ensure_complete_type(ira->codegen, type_entry))) | 17135 | ConstExprValue *inner_fields = create_const_vals(2); |
| 17113 | return nullptr; | 17136 | inner_fields[1].special = ConstValSpecialStatic; |
| 17137 | inner_fields[1].type = ira->codegen->builtin_types.entry_usize; | ||
| 17114 | 17138 | ||
| 17115 | const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field, | 17139 | ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); |
| 17116 | TypeTableEntry *type_info_enum_field_type) { | 17140 | init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); |
| 17117 | enum_field_val->special = ConstValSpecialStatic; | ||
| 17118 | enum_field_val->type = type_info_enum_field_type; | ||
| 17119 | 17141 | ||
| 17120 | ConstExprValue *inner_fields = create_const_vals(2); | 17142 | bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); |
| 17121 | inner_fields[1].special = ConstValSpecialStatic; | ||
| 17122 | inner_fields[1].type = ira->codegen->builtin_types.entry_usize; | ||
| 17123 | 17143 | ||
| 17124 | ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); | 17144 | enum_field_val->data.x_struct.fields = inner_fields; |
| 17125 | init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); | 17145 | } |
| 17126 | 17146 | ||
| 17127 | bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value); | 17147 | static Error ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry, ConstExprValue **out) { |
| 17148 | Error err; | ||
| 17149 | assert(type_entry != nullptr); | ||
| 17150 | assert(!type_is_invalid(type_entry)); | ||
| 17128 | 17151 | ||
| 17129 | enum_field_val->data.x_struct.fields = inner_fields; | 17152 | if ((err = ensure_complete_type(ira->codegen, type_entry))) |
| 17130 | }; | 17153 | return err; |
| 17131 | 17154 | ||
| 17132 | if (type_entry == ira->codegen->builtin_types.entry_global_error_set) { | 17155 | if (type_entry == ira->codegen->builtin_types.entry_global_error_set) { |
| 17133 | zig_panic("TODO implement @typeInfo for global error set"); | 17156 | zig_panic("TODO implement @typeInfo for global error set"); |
| ... | @@ -17150,13 +17173,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17150,13 +17173,16 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17150 | case TypeTableEntryIdBlock: | 17173 | case TypeTableEntryIdBlock: |
| 17151 | case TypeTableEntryIdArgTuple: | 17174 | case TypeTableEntryIdArgTuple: |
| 17152 | case TypeTableEntryIdOpaque: | 17175 | case TypeTableEntryIdOpaque: |
| 17153 | return nullptr; | 17176 | *out = nullptr; |
| 17177 | return ErrorNone; | ||
| 17154 | default: | 17178 | default: |
| 17155 | { | 17179 | { |
| 17156 | // Lookup an available value in our cache. | 17180 | // Lookup an available value in our cache. |
| 17157 | auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); | 17181 | auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); |
| 17158 | if (entry != nullptr) | 17182 | if (entry != nullptr) { |
| 17159 | return entry->value; | 17183 | *out = entry->value; |
| 17184 | return ErrorNone; | ||
| 17185 | } | ||
| 17160 | 17186 | ||
| 17161 | // Fallthrough if we don't find one. | 17187 | // Fallthrough if we don't find one. |
| 17162 | } | 17188 | } |
| ... | @@ -17307,15 +17333,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17307,15 +17333,15 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17307 | { | 17333 | { |
| 17308 | TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; | 17334 | TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; |
| 17309 | ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; | 17335 | ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index]; |
| 17310 | make_enum_field_val(enum_field_val, enum_field, type_info_enum_field_type); | 17336 | make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); |
| 17311 | enum_field_val->data.x_struct.parent.id = ConstParentIdArray; | 17337 | enum_field_val->data.x_struct.parent.id = ConstParentIdArray; |
| 17312 | enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; | 17338 | enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; |
| 17313 | enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; | 17339 | enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; |
| 17314 | } | 17340 | } |
| 17315 | // defs: []TypeInfo.Definition | 17341 | // defs: []TypeInfo.Definition |
| 17316 | ensure_field_index(result->type, "defs", 3); | 17342 | ensure_field_index(result->type, "defs", 3); |
| 17317 | if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope)) | 17343 | if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope))) |
| 17318 | return nullptr; | 17344 | return err; |
| 17319 | 17345 | ||
| 17320 | break; | 17346 | break; |
| 17321 | } | 17347 | } |
| ... | @@ -17341,8 +17367,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17341,8 +17367,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17341 | error_array->data.x_array.s_none.elements = create_const_vals(error_count); | 17367 | error_array->data.x_array.s_none.elements = create_const_vals(error_count); |
| 17342 | 17368 | ||
| 17343 | init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); | 17369 | init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); |
| 17344 | for (uint32_t error_index = 0; error_index < error_count; error_index++) | 17370 | for (uint32_t error_index = 0; error_index < error_count; error_index++) { |
| 17345 | { | ||
| 17346 | ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; | 17371 | ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; |
| 17347 | ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index]; | 17372 | ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index]; |
| 17348 | 17373 | ||
| ... | @@ -17420,9 +17445,9 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17420,9 +17445,9 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17420 | tag_type->type = ira->codegen->builtin_types.entry_type; | 17445 | tag_type->type = ira->codegen->builtin_types.entry_type; |
| 17421 | tag_type->data.x_type = type_entry->data.unionation.tag_type; | 17446 | tag_type->data.x_type = type_entry->data.unionation.tag_type; |
| 17422 | fields[1].data.x_optional = tag_type; | 17447 | fields[1].data.x_optional = tag_type; |
| 17423 | } | 17448 | } else { |
| 17424 | else | ||
| 17425 | fields[1].data.x_optional = nullptr; | 17449 | fields[1].data.x_optional = nullptr; |
| 17450 | } | ||
| 17426 | // fields: []TypeInfo.UnionField | 17451 | // fields: []TypeInfo.UnionField |
| 17427 | ensure_field_index(result->type, "fields", 2); | 17452 | ensure_field_index(result->type, "fields", 2); |
| 17428 | 17453 | ||
| ... | @@ -17455,7 +17480,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17455,7 +17480,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17455 | inner_fields[1].data.x_optional = nullptr; | 17480 | inner_fields[1].data.x_optional = nullptr; |
| 17456 | } else { | 17481 | } else { |
| 17457 | inner_fields[1].data.x_optional = create_const_vals(1); | 17482 | inner_fields[1].data.x_optional = create_const_vals(1); |
| 17458 | make_enum_field_val(inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type); | 17483 | make_enum_field_val(ira, inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type); |
| 17459 | } | 17484 | } |
| 17460 | 17485 | ||
| 17461 | inner_fields[2].special = ConstValSpecialStatic; | 17486 | inner_fields[2].special = ConstValSpecialStatic; |
| ... | @@ -17472,8 +17497,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17472,8 +17497,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17472 | } | 17497 | } |
| 17473 | // defs: []TypeInfo.Definition | 17498 | // defs: []TypeInfo.Definition |
| 17474 | ensure_field_index(result->type, "defs", 3); | 17499 | ensure_field_index(result->type, "defs", 3); |
| 17475 | if (!ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope)) | 17500 | if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope))) |
| 17476 | return nullptr; | 17501 | return err; |
| 17477 | 17502 | ||
| 17478 | break; | 17503 | break; |
| 17479 | } | 17504 | } |
| ... | @@ -17546,8 +17571,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17546,8 +17571,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17546 | } | 17571 | } |
| 17547 | // defs: []TypeInfo.Definition | 17572 | // defs: []TypeInfo.Definition |
| 17548 | ensure_field_index(result->type, "defs", 2); | 17573 | ensure_field_index(result->type, "defs", 2); |
| 17549 | if (!ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope)) | 17574 | if ((err = ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope))) |
| 17550 | return nullptr; | 17575 | return err; |
| 17551 | 17576 | ||
| 17552 | break; | 17577 | break; |
| 17553 | } | 17578 | } |
| ... | @@ -17660,7 +17685,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17660,7 +17685,8 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17660 | { | 17685 | { |
| 17661 | TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type; | 17686 | TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type; |
| 17662 | assert(fn_type->id == TypeTableEntryIdFn); | 17687 | assert(fn_type->id == TypeTableEntryIdFn); |
| 17663 | result = ir_make_type_info_value(ira, fn_type); | 17688 | if ((err = ir_make_type_info_value(ira, fn_type, &result))) |
| 17689 | return err; | ||
| 17664 | 17690 | ||
| 17665 | break; | 17691 | break; |
| 17666 | } | 17692 | } |
| ... | @@ -17668,12 +17694,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t | ... | @@ -17668,12 +17694,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t |
| 17668 | 17694 | ||
| 17669 | assert(result != nullptr); | 17695 | assert(result != nullptr); |
| 17670 | ira->codegen->type_info_cache.put(type_entry, result); | 17696 | ira->codegen->type_info_cache.put(type_entry, result); |
| 17671 | return result; | 17697 | *out = result; |
| 17698 | return ErrorNone; | ||
| 17672 | } | 17699 | } |
| 17673 | 17700 | ||
| 17674 | static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, | 17701 | static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, |
| 17675 | IrInstructionTypeInfo *instruction) | 17702 | IrInstructionTypeInfo *instruction) |
| 17676 | { | 17703 | { |
| 17704 | Error err; | ||
| 17677 | IrInstruction *type_value = instruction->type_value->other; | 17705 | IrInstruction *type_value = instruction->type_value->other; |
| 17678 | TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); | 17706 | TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); |
| 17679 | if (type_is_invalid(type_entry)) | 17707 | if (type_is_invalid(type_entry)) |
| ... | @@ -17681,15 +17709,16 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, | ... | @@ -17681,15 +17709,16 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira, |
| 17681 | 17709 | ||
| 17682 | TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr, nullptr); | 17710 | TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr, nullptr); |
| 17683 | 17711 | ||
| 17712 | ConstExprValue *payload; | ||
| 17713 | if ((err = ir_make_type_info_value(ira, type_entry, &payload))) | ||
| 17714 | return ira->codegen->builtin_types.entry_invalid; | ||
| 17715 | |||
| 17684 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | 17716 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); |
| 17685 | out_val->type = result_type; | 17717 | out_val->type = result_type; |
| 17686 | bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); | 17718 | bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); |
| 17687 | |||
| 17688 | ConstExprValue *payload = ir_make_type_info_value(ira, type_entry); | ||
| 17689 | out_val->data.x_union.payload = payload; | 17719 | out_val->data.x_union.payload = payload; |
| 17690 | 17720 | ||
| 17691 | if (payload != nullptr) | 17721 | if (payload != nullptr) { |
| 17692 | { | ||
| 17693 | assert(payload->type->id == TypeTableEntryIdStruct); | 17722 | assert(payload->type->id == TypeTableEntryIdStruct); |
| 17694 | payload->data.x_struct.parent.id = ConstParentIdUnion; | 17723 | payload->data.x_struct.parent.id = ConstParentIdUnion; |
| 17695 | payload->data.x_struct.parent.data.p_union.union_val = out_val; | 17724 | payload->data.x_struct.parent.data.p_union.union_val = out_val; |
| ... | @@ -19731,6 +19760,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 | ... | @@ -19731,6 +19760,8 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 |
| 19731 | } | 19760 | } |
| 19732 | 19761 | ||
| 19733 | static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { | 19762 | static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { |
| 19763 | Error err; | ||
| 19764 | |||
| 19734 | IrInstruction *dest_type_value = instruction->dest_type->other; | 19765 | IrInstruction *dest_type_value = instruction->dest_type->other; |
| 19735 | TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value); | 19766 | TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value); |
| 19736 | if (type_is_invalid(dest_type)) | 19767 | if (type_is_invalid(dest_type)) |
| ... | @@ -19784,9 +19815,13 @@ static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruc | ... | @@ -19784,9 +19815,13 @@ static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruc |
| 19784 | instruction->base.source_node, nullptr, ptr); | 19815 | instruction->base.source_node, nullptr, ptr); |
| 19785 | casted_ptr->value.type = dest_type; | 19816 | casted_ptr->value.type = dest_type; |
| 19786 | 19817 | ||
| 19787 | // keep the bigger alignment, it can only help | 19818 | // Keep the bigger alignment, it can only help- |
| 19819 | // unless the target is zero bits. | ||
| 19820 | if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type))) | ||
| 19821 | return ira->codegen->builtin_types.entry_invalid; | ||
| 19822 | |||
| 19788 | IrInstruction *result; | 19823 | IrInstruction *result; |
| 19789 | if (src_align_bytes > dest_align_bytes) { | 19824 | if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) { |
| 19790 | result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); | 19825 | result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); |
| 19791 | if (type_is_invalid(result->value.type)) | 19826 | if (type_is_invalid(result->value.type)) |
| 19792 | return ira->codegen->builtin_types.entry_invalid; | 19827 | return ira->codegen->builtin_types.entry_invalid; |
| ... | @@ -21192,8 +21227,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl | ... | @@ -21192,8 +21227,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl |
| 21192 | ira->new_irb.codegen = codegen; | 21227 | ira->new_irb.codegen = codegen; |
| 21193 | ira->new_irb.exec = new_exec; | 21228 | ira->new_irb.exec = new_exec; |
| 21194 | 21229 | ||
| 21195 | ira->exec_context.mem_slot_count = ira->old_irb.exec->mem_slot_count; | 21230 | ConstExprValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count); |
| 21196 | ira->exec_context.mem_slot_list = create_const_vals(ira->exec_context.mem_slot_count); | 21231 | ira->exec_context.mem_slot_list.resize(ira->old_irb.exec->mem_slot_count); |
| 21232 | for (size_t i = 0; i < ira->exec_context.mem_slot_list.length; i += 1) { | ||
| 21233 | ira->exec_context.mem_slot_list.items[i] = &vals[i]; | ||
| 21234 | } | ||
| 21197 | 21235 | ||
| 21198 | IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); | 21236 | IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); |
| 21199 | IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr); | 21237 | IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr); |
src/translate_c.cpp+10-11| ... | @@ -3075,12 +3075,19 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, | ... | @@ -3075,12 +3075,19 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, |
| 3075 | trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt)); | 3075 | trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt)); |
| 3076 | case Stmt::DeclStmtClass: | 3076 | case Stmt::DeclStmtClass: |
| 3077 | return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); | 3077 | return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope); |
| 3078 | case Stmt::DoStmtClass: | ||
| 3078 | case Stmt::WhileStmtClass: { | 3079 | case Stmt::WhileStmtClass: { |
| 3079 | AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt); | 3080 | AstNode *while_node = sc == Stmt::DoStmtClass |
| 3081 | ? trans_do_loop(c, scope, (const DoStmt *)stmt) | ||
| 3082 | : trans_while_loop(c, scope, (const WhileStmt *)stmt); | ||
| 3083 | |||
| 3084 | if (while_node == nullptr) | ||
| 3085 | return ErrorUnexpected; | ||
| 3086 | |||
| 3080 | assert(while_node->type == NodeTypeWhileExpr); | 3087 | assert(while_node->type == NodeTypeWhileExpr); |
| 3081 | if (while_node->data.while_expr.body == nullptr) { | 3088 | if (while_node->data.while_expr.body == nullptr) |
| 3082 | while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); | 3089 | while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); |
| 3083 | } | 3090 | |
| 3084 | return wrap_stmt(out_node, out_child_scope, scope, while_node); | 3091 | return wrap_stmt(out_node, out_child_scope, scope, while_node); |
| 3085 | } | 3092 | } |
| 3086 | case Stmt::IfStmtClass: | 3093 | case Stmt::IfStmtClass: |
| ... | @@ -3105,14 +3112,6 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, | ... | @@ -3105,14 +3112,6 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, |
| 3105 | case Stmt::UnaryExprOrTypeTraitExprClass: | 3112 | case Stmt::UnaryExprOrTypeTraitExprClass: |
| 3106 | return wrap_stmt(out_node, out_child_scope, scope, | 3113 | return wrap_stmt(out_node, out_child_scope, scope, |
| 3107 | trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); | 3114 | trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt)); |
| 3108 | case Stmt::DoStmtClass: { | ||
| 3109 | AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt); | ||
| 3110 | assert(while_node->type == NodeTypeWhileExpr); | ||
| 3111 | if (while_node->data.while_expr.body == nullptr) { | ||
| 3112 | while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock); | ||
| 3113 | } | ||
| 3114 | return wrap_stmt(out_node, out_child_scope, scope, while_node); | ||
| 3115 | } | ||
| 3116 | case Stmt::ForStmtClass: { | 3115 | case Stmt::ForStmtClass: { |
| 3117 | AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt); | 3116 | AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt); |
| 3118 | return wrap_stmt(out_node, out_child_scope, scope, node); | 3117 | return wrap_stmt(out_node, out_child_scope, scope, node); |
src/windows_sdk.cpp+7-2| ... | @@ -204,7 +204,11 @@ static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) { | ... | @@ -204,7 +204,11 @@ static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) { |
| 204 | // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive | 204 | // https://developer.microsoft.com/en-us/windows/downloads/sdk-archive |
| 205 | c2 = 26624; | 205 | c2 = 26624; |
| 206 | } | 206 | } |
| 207 | if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) { | 207 | |
| 208 | if ( (c0 > v0) | ||
| 209 | || (c0 == v0 && c1 > v1) | ||
| 210 | || (c0 == v0 && c1 == v1 && c2 > v2) | ||
| 211 | || (c0 == v0 && c1 == v1 && c2 == v2 && c3 > v3) ) { | ||
| 208 | v0 = c0, v1 = c1, v2 = c2, v3 = c3; | 212 | v0 = c0, v1 = c1, v2 = c2, v3 = c3; |
| 209 | free((void*)priv->base.version10_ptr); | 213 | free((void*)priv->base.version10_ptr); |
| 210 | priv->base.version10_ptr = strdup(ffd.cFileName); | 214 | priv->base.version10_ptr = strdup(ffd.cFileName); |
| ... | @@ -244,7 +248,8 @@ static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) { | ... | @@ -244,7 +248,8 @@ static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) { |
| 244 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | 248 | if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { |
| 245 | int c0 = 0, c1 = 0; | 249 | int c0 = 0, c1 = 0; |
| 246 | sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); | 250 | sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1); |
| 247 | if ((c0 > v0) || (c1 > v1)) { | 251 | |
| 252 | if ( (c0 > v0) || (c0 == v0 && c1 > v1) ) { | ||
| 248 | v0 = c0, v1 = c1; | 253 | v0 = c0, v1 = c1; |
| 249 | free((void*)priv->base.version81_ptr); | 254 | free((void*)priv->base.version81_ptr); |
| 250 | priv->base.version81_ptr = strdup(ffd.cFileName); | 255 | priv->base.version81_ptr = strdup(ffd.cFileName); |
std/coff.zig created+230| ... | @@ -0,0 +1,230 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("index.zig"); | ||
| 3 | const io = std.io; | ||
| 4 | const mem = std.mem; | ||
| 5 | const os = std.os; | ||
| 6 | |||
| 7 | const ArrayList = std.ArrayList; | ||
| 8 | |||
| 9 | // CoffHeader.machine values | ||
| 10 | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx | ||
| 11 | const IMAGE_FILE_MACHINE_I386 = 0x014c; | ||
| 12 | const IMAGE_FILE_MACHINE_IA64 = 0x0200; | ||
| 13 | const IMAGE_FILE_MACHINE_AMD64 = 0x8664; | ||
| 14 | |||
| 15 | // OptionalHeader.magic values | ||
| 16 | // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx | ||
| 17 | const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; | ||
| 18 | const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; | ||
| 19 | |||
| 20 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | ||
| 21 | const DEBUG_DIRECTORY = 6; | ||
| 22 | |||
| 23 | pub const CoffError = error { | ||
| 24 | InvalidPEMagic, | ||
| 25 | InvalidPEHeader, | ||
| 26 | InvalidMachine, | ||
| 27 | MissingCoffSection, | ||
| 28 | }; | ||
| 29 | |||
| 30 | pub const Coff = struct { | ||
| 31 | in_file: os.File, | ||
| 32 | allocator: *mem.Allocator, | ||
| 33 | |||
| 34 | coff_header: CoffHeader, | ||
| 35 | pe_header: OptionalHeader, | ||
| 36 | sections: ArrayList(Section), | ||
| 37 | |||
| 38 | guid: [16]u8, | ||
| 39 | age: u32, | ||
| 40 | |||
| 41 | pub fn loadHeader(self: *Coff) !void { | ||
| 42 | const pe_pointer_offset = 0x3C; | ||
| 43 | |||
| 44 | var file_stream = io.FileInStream.init(self.in_file); | ||
| 45 | const in = &file_stream.stream; | ||
| 46 | |||
| 47 | var magic: [2]u8 = undefined; | ||
| 48 | try in.readNoEof(magic[0..]); | ||
| 49 | if (!mem.eql(u8, magic, "MZ")) | ||
| 50 | return error.InvalidPEMagic; | ||
| 51 | |||
| 52 | // Seek to PE File Header (coff header) | ||
| 53 | try self.in_file.seekTo(pe_pointer_offset); | ||
| 54 | const pe_magic_offset = try in.readIntLe(u32); | ||
| 55 | try self.in_file.seekTo(pe_magic_offset); | ||
| 56 | |||
| 57 | var pe_header_magic: [4]u8 = undefined; | ||
| 58 | try in.readNoEof(pe_header_magic[0..]); | ||
| 59 | if (!mem.eql(u8, pe_header_magic, []u8{'P', 'E', 0, 0})) | ||
| 60 | return error.InvalidPEHeader; | ||
| 61 | |||
| 62 | self.coff_header = CoffHeader { | ||
| 63 | .machine = try in.readIntLe(u16), | ||
| 64 | .number_of_sections = try in.readIntLe(u16), | ||
| 65 | .timedate_stamp = try in.readIntLe(u32), | ||
| 66 | .pointer_to_symbol_table = try in.readIntLe(u32), | ||
| 67 | .number_of_symbols = try in.readIntLe(u32), | ||
| 68 | .size_of_optional_header = try in.readIntLe(u16), | ||
| 69 | .characteristics = try in.readIntLe(u16), | ||
| 70 | }; | ||
| 71 | |||
| 72 | switch (self.coff_header.machine) { | ||
| 73 | IMAGE_FILE_MACHINE_I386, | ||
| 74 | IMAGE_FILE_MACHINE_AMD64, | ||
| 75 | IMAGE_FILE_MACHINE_IA64 | ||
| 76 | => {}, | ||
| 77 | else => return error.InvalidMachine, | ||
| 78 | } | ||
| 79 | |||
| 80 | try self.loadOptionalHeader(&file_stream); | ||
| 81 | } | ||
| 82 | |||
| 83 | fn loadOptionalHeader(self: *Coff, file_stream: *io.FileInStream) !void { | ||
| 84 | const in = &file_stream.stream; | ||
| 85 | self.pe_header.magic = try in.readIntLe(u16); | ||
| 86 | // For now we're only interested in finding the reference to the .pdb, | ||
| 87 | // so we'll skip most of this header, which size is different in 32 | ||
| 88 | // 64 bits by the way. | ||
| 89 | var skip_size: u16 = undefined; | ||
| 90 | if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { | ||
| 91 | skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 18 * @sizeOf(u32); | ||
| 92 | } | ||
| 93 | else if (self.pe_header.magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { | ||
| 94 | skip_size = 2 * @sizeOf(u8) + 8 * @sizeOf(u16) + 12 * @sizeOf(u32) + 5 * @sizeOf(u64); | ||
| 95 | } | ||
| 96 | else | ||
| 97 | return error.InvalidPEMagic; | ||
| 98 | |||
| 99 | try self.in_file.seekForward(skip_size); | ||
| 100 | |||
| 101 | const number_of_rva_and_sizes = try in.readIntLe(u32); | ||
| 102 | if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES) | ||
| 103 | return error.InvalidPEHeader; | ||
| 104 | |||
| 105 | for (self.pe_header.data_directory) |*data_dir| { | ||
| 106 | data_dir.* = OptionalHeader.DataDirectory { | ||
| 107 | .virtual_address = try in.readIntLe(u32), | ||
| 108 | .size = try in.readIntLe(u32), | ||
| 109 | }; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { | ||
| 114 | try self.loadSections(); | ||
| 115 | const header = (self.getSection(".rdata") orelse return error.MissingCoffSection).header; | ||
| 116 | |||
| 117 | // The linker puts a chunk that contains the .pdb path right after the | ||
| 118 | // debug_directory. | ||
| 119 | const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; | ||
| 120 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; | ||
| 121 | try self.in_file.seekTo(file_offset + debug_dir.size); | ||
| 122 | |||
| 123 | var file_stream = io.FileInStream.init(self.in_file); | ||
| 124 | const in = &file_stream.stream; | ||
| 125 | |||
| 126 | var cv_signature: [4]u8 = undefined; // CodeView signature | ||
| 127 | try in.readNoEof(cv_signature[0..]); | ||
| 128 | // 'RSDS' indicates PDB70 format, used by lld. | ||
| 129 | if (!mem.eql(u8, cv_signature, "RSDS")) | ||
| 130 | return error.InvalidPEMagic; | ||
| 131 | try in.readNoEof(self.guid[0..]); | ||
| 132 | self.age = try in.readIntLe(u32); | ||
| 133 | |||
| 134 | // Finally read the null-terminated string. | ||
| 135 | var byte = try in.readByte(); | ||
| 136 | var i: usize = 0; | ||
| 137 | while (byte != 0 and i < buffer.len) : (i += 1) { | ||
| 138 | buffer[i] = byte; | ||
| 139 | byte = try in.readByte(); | ||
| 140 | } | ||
| 141 | |||
| 142 | if (byte != 0 and i == buffer.len) | ||
| 143 | return error.NameTooLong; | ||
| 144 | |||
| 145 | return i; | ||
| 146 | } | ||
| 147 | |||
| 148 | pub fn loadSections(self: *Coff) !void { | ||
| 149 | if (self.sections.len != 0) | ||
| 150 | return; | ||
| 151 | |||
| 152 | self.sections = ArrayList(Section).init(self.allocator); | ||
| 153 | |||
| 154 | var file_stream = io.FileInStream.init(self.in_file); | ||
| 155 | const in = &file_stream.stream; | ||
| 156 | |||
| 157 | var name: [8]u8 = undefined; | ||
| 158 | |||
| 159 | var i: u16 = 0; | ||
| 160 | while (i < self.coff_header.number_of_sections) : (i += 1) { | ||
| 161 | try in.readNoEof(name[0..]); | ||
| 162 | try self.sections.append(Section { | ||
| 163 | .header = SectionHeader { | ||
| 164 | .name = name, | ||
| 165 | .misc = SectionHeader.Misc { .physical_address = try in.readIntLe(u32) }, | ||
| 166 | .virtual_address = try in.readIntLe(u32), | ||
| 167 | .size_of_raw_data = try in.readIntLe(u32), | ||
| 168 | .pointer_to_raw_data = try in.readIntLe(u32), | ||
| 169 | .pointer_to_relocations = try in.readIntLe(u32), | ||
| 170 | .pointer_to_line_numbers = try in.readIntLe(u32), | ||
| 171 | .number_of_relocations = try in.readIntLe(u16), | ||
| 172 | .number_of_line_numbers = try in.readIntLe(u16), | ||
| 173 | .characteristics = try in.readIntLe(u32), | ||
| 174 | }, | ||
| 175 | }); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | pub fn getSection(self: *Coff, comptime name: []const u8) ?*Section { | ||
| 180 | for (self.sections.toSlice()) |*sec| { | ||
| 181 | if (mem.eql(u8, sec.header.name[0..name.len], name)) { | ||
| 182 | return sec; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | return null; | ||
| 186 | } | ||
| 187 | |||
| 188 | }; | ||
| 189 | |||
| 190 | const CoffHeader = struct { | ||
| 191 | machine: u16, | ||
| 192 | number_of_sections: u16, | ||
| 193 | timedate_stamp: u32, | ||
| 194 | pointer_to_symbol_table: u32, | ||
| 195 | number_of_symbols: u32, | ||
| 196 | size_of_optional_header: u16, | ||
| 197 | characteristics: u16 | ||
| 198 | }; | ||
| 199 | |||
| 200 | const OptionalHeader = struct { | ||
| 201 | const DataDirectory = struct { | ||
| 202 | virtual_address: u32, | ||
| 203 | size: u32 | ||
| 204 | }; | ||
| 205 | |||
| 206 | magic: u16, | ||
| 207 | data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, | ||
| 208 | }; | ||
| 209 | |||
| 210 | pub const Section = struct { | ||
| 211 | header: SectionHeader, | ||
| 212 | }; | ||
| 213 | |||
| 214 | const SectionHeader = struct { | ||
| 215 | const Misc = union { | ||
| 216 | physical_address: u32, | ||
| 217 | virtual_size: u32 | ||
| 218 | }; | ||
| 219 | |||
| 220 | name: [8]u8, | ||
| 221 | misc: Misc, | ||
| 222 | virtual_address: u32, | ||
| 223 | size_of_raw_data: u32, | ||
| 224 | pointer_to_raw_data: u32, | ||
| 225 | pointer_to_relocations: u32, | ||
| 226 | pointer_to_line_numbers: u32, | ||
| 227 | number_of_relocations: u16, | ||
| 228 | number_of_line_numbers: u16, | ||
| 229 | characteristics: u32, | ||
| 230 | }; | ||
std/crypto/blake2.zig+8-8| ... | @@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256); | ... | @@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256); |
| 34 | fn Blake2s(comptime out_len: usize) type { | 34 | fn Blake2s(comptime out_len: usize) type { |
| 35 | return struct { | 35 | return struct { |
| 36 | const Self = this; | 36 | const Self = this; |
| 37 | const block_size = 64; | 37 | const block_length = 64; |
| 38 | const digest_size = out_len / 8; | 38 | const digest_length = out_len / 8; |
| 39 | 39 | ||
| 40 | const iv = [8]u32{ | 40 | const iv = [8]u32{ |
| 41 | 0x6A09E667, | 41 | 0x6A09E667, |
| ... | @@ -250,8 +250,8 @@ test "blake2s256 streaming" { | ... | @@ -250,8 +250,8 @@ test "blake2s256 streaming" { |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | test "blake2s256 aligned final" { | 252 | test "blake2s256 aligned final" { |
| 253 | var block = []u8{0} ** Blake2s256.block_size; | 253 | var block = []u8{0} ** Blake2s256.block_length; |
| 254 | var out: [Blake2s256.digest_size]u8 = undefined; | 254 | var out: [Blake2s256.digest_length]u8 = undefined; |
| 255 | 255 | ||
| 256 | var h = Blake2s256.init(); | 256 | var h = Blake2s256.init(); |
| 257 | h.update(block); | 257 | h.update(block); |
| ... | @@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512); | ... | @@ -267,8 +267,8 @@ pub const Blake2b512 = Blake2b(512); |
| 267 | fn Blake2b(comptime out_len: usize) type { | 267 | fn Blake2b(comptime out_len: usize) type { |
| 268 | return struct { | 268 | return struct { |
| 269 | const Self = this; | 269 | const Self = this; |
| 270 | const block_size = 128; | 270 | const block_length = 128; |
| 271 | const digest_size = out_len / 8; | 271 | const digest_length = out_len / 8; |
| 272 | 272 | ||
| 273 | const iv = [8]u64{ | 273 | const iv = [8]u64{ |
| 274 | 0x6a09e667f3bcc908, | 274 | 0x6a09e667f3bcc908, |
| ... | @@ -483,8 +483,8 @@ test "blake2b512 streaming" { | ... | @@ -483,8 +483,8 @@ test "blake2b512 streaming" { |
| 483 | } | 483 | } |
| 484 | 484 | ||
| 485 | test "blake2b512 aligned final" { | 485 | test "blake2b512 aligned final" { |
| 486 | var block = []u8{0} ** Blake2b512.block_size; | 486 | var block = []u8{0} ** Blake2b512.block_length; |
| 487 | var out: [Blake2b512.digest_size]u8 = undefined; | 487 | var out: [Blake2b512.digest_length]u8 = undefined; |
| 488 | 488 | ||
| 489 | var h = Blake2b512.init(); | 489 | var h = Blake2b512.init(); |
| 490 | h.update(block); | 490 | h.update(block); |
std/crypto/chacha20.zig created+432| ... | @@ -0,0 +1,432 @@ | ||
| 1 | // Based on public domain Supercop by Daniel J. Bernstein | ||
| 2 | |||
| 3 | const std = @import("../index.zig"); | ||
| 4 | const mem = std.mem; | ||
| 5 | const endian = std.endian; | ||
| 6 | const assert = std.debug.assert; | ||
| 7 | const builtin = @import("builtin"); | ||
| 8 | |||
| 9 | const QuarterRound = struct { | ||
| 10 | a: usize, | ||
| 11 | b: usize, | ||
| 12 | c: usize, | ||
| 13 | d: usize, | ||
| 14 | }; | ||
| 15 | |||
| 16 | fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound { | ||
| 17 | return QuarterRound{ | ||
| 18 | .a = a, | ||
| 19 | .b = b, | ||
| 20 | .c = c, | ||
| 21 | .d = d, | ||
| 22 | }; | ||
| 23 | } | ||
| 24 | |||
| 25 | // The chacha family of ciphers are based on the salsa family. | ||
| 26 | fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { | ||
| 27 | assert(out.len >= 64); | ||
| 28 | |||
| 29 | var x: [16]u32 = undefined; | ||
| 30 | |||
| 31 | for (x) |_, i| | ||
| 32 | x[i] = input[i]; | ||
| 33 | |||
| 34 | const rounds = comptime []QuarterRound{ | ||
| 35 | Rp(0, 4, 8, 12), | ||
| 36 | Rp(1, 5, 9, 13), | ||
| 37 | Rp(2, 6, 10, 14), | ||
| 38 | Rp(3, 7, 11, 15), | ||
| 39 | Rp(0, 5, 10, 15), | ||
| 40 | Rp(1, 6, 11, 12), | ||
| 41 | Rp(2, 7, 8, 13), | ||
| 42 | Rp(3, 4, 9, 14), | ||
| 43 | }; | ||
| 44 | |||
| 45 | comptime var j: usize = 0; | ||
| 46 | inline while (j < 20) : (j += 2) { | ||
| 47 | // two-round cycles | ||
| 48 | inline for (rounds) |r| { | ||
| 49 | x[r.a] +%= x[r.b]; | ||
| 50 | x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(16)); | ||
| 51 | x[r.c] +%= x[r.d]; | ||
| 52 | x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(12)); | ||
| 53 | x[r.a] +%= x[r.b]; | ||
| 54 | x[r.d] = std.math.rotl(u32, x[r.d] ^ x[r.a], u32(8)); | ||
| 55 | x[r.c] +%= x[r.d]; | ||
| 56 | x[r.b] = std.math.rotl(u32, x[r.b] ^ x[r.c], u32(7)); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | for (x) |_, i| { | ||
| 61 | mem.writeInt(out[4 * i .. 4 * i + 4], x[i] +% input[i], builtin.Endian.Little); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) void { | ||
| 66 | var ctx: [16]u32 = undefined; | ||
| 67 | var remaining: usize = if (in.len > out.len) in.len else out.len; | ||
| 68 | var cursor: usize = 0; | ||
| 69 | |||
| 70 | const c = "expand 32-byte k"; | ||
| 71 | const constant_le = []u32{ | ||
| 72 | mem.readIntLE(u32, c[0..4]), | ||
| 73 | mem.readIntLE(u32, c[4..8]), | ||
| 74 | mem.readIntLE(u32, c[8..12]), | ||
| 75 | mem.readIntLE(u32, c[12..16]), | ||
| 76 | }; | ||
| 77 | |||
| 78 | mem.copy(u32, ctx[0..], constant_le[0..4]); | ||
| 79 | mem.copy(u32, ctx[4..12], key[0..8]); | ||
| 80 | mem.copy(u32, ctx[12..16], counter[0..4]); | ||
| 81 | |||
| 82 | while (true) { | ||
| 83 | var buf: [64]u8 = undefined; | ||
| 84 | salsa20_wordtobyte(buf[0..], ctx); | ||
| 85 | |||
| 86 | if (remaining < 64) { | ||
| 87 | var i: usize = 0; | ||
| 88 | while (i < remaining) : (i += 1) | ||
| 89 | out[cursor + i] = in[cursor + i] ^ buf[i]; | ||
| 90 | return; | ||
| 91 | } | ||
| 92 | |||
| 93 | var i: usize = 0; | ||
| 94 | while (i < 64) : (i += 1) | ||
| 95 | out[cursor + i] = in[cursor + i] ^ buf[i]; | ||
| 96 | |||
| 97 | cursor += 64; | ||
| 98 | remaining -= 64; | ||
| 99 | |||
| 100 | ctx[12] += 1; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | /// ChaCha20 avoids the possibility of timing attacks, as there are no branches | ||
| 105 | /// on secret key data. | ||
| 106 | /// | ||
| 107 | /// in and out should be the same length. | ||
| 108 | /// counter should generally be 0 or 1 | ||
| 109 | /// | ||
| 110 | /// ChaCha20 is self-reversing. To decrypt just run the cipher with the same | ||
| 111 | /// counter, nonce, and key. | ||
| 112 | pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void { | ||
| 113 | assert(in.len >= out.len); | ||
| 114 | assert((in.len >> 6) + counter <= @maxValue(u32)); | ||
| 115 | |||
| 116 | var k: [8]u32 = undefined; | ||
| 117 | var c: [4]u32 = undefined; | ||
| 118 | |||
| 119 | k[0] = mem.readIntLE(u32, key[0..4]); | ||
| 120 | k[1] = mem.readIntLE(u32, key[4..8]); | ||
| 121 | k[2] = mem.readIntLE(u32, key[8..12]); | ||
| 122 | k[3] = mem.readIntLE(u32, key[12..16]); | ||
| 123 | k[4] = mem.readIntLE(u32, key[16..20]); | ||
| 124 | k[5] = mem.readIntLE(u32, key[20..24]); | ||
| 125 | k[6] = mem.readIntLE(u32, key[24..28]); | ||
| 126 | k[7] = mem.readIntLE(u32, key[28..32]); | ||
| 127 | |||
| 128 | c[0] = counter; | ||
| 129 | c[1] = mem.readIntLE(u32, nonce[0..4]); | ||
| 130 | c[2] = mem.readIntLE(u32, nonce[4..8]); | ||
| 131 | c[3] = mem.readIntLE(u32, nonce[8..12]); | ||
| 132 | chaCha20_internal(out, in, k, c); | ||
| 133 | } | ||
| 134 | |||
| 135 | /// This is the original ChaCha20 before RFC 7539, which recommends using the | ||
| 136 | /// orgininal version on applications such as disk or file encryption that might | ||
| 137 | /// exceed the 256 GiB limit of the 96-bit nonce version. | ||
| 138 | pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void { | ||
| 139 | assert(in.len >= out.len); | ||
| 140 | assert(counter +% (in.len >> 6) >= counter); | ||
| 141 | |||
| 142 | var cursor: u64 = 0; | ||
| 143 | var k: [8]u32 = undefined; | ||
| 144 | var c: [4]u32 = undefined; | ||
| 145 | |||
| 146 | k[0] = mem.readIntLE(u32, key[0..4]); | ||
| 147 | k[1] = mem.readIntLE(u32, key[4..8]); | ||
| 148 | k[2] = mem.readIntLE(u32, key[8..12]); | ||
| 149 | k[3] = mem.readIntLE(u32, key[12..16]); | ||
| 150 | k[4] = mem.readIntLE(u32, key[16..20]); | ||
| 151 | k[5] = mem.readIntLE(u32, key[20..24]); | ||
| 152 | k[6] = mem.readIntLE(u32, key[24..28]); | ||
| 153 | k[7] = mem.readIntLE(u32, key[28..32]); | ||
| 154 | |||
| 155 | c[0] = @truncate(u32, counter); | ||
| 156 | c[1] = @truncate(u32, counter >> 32); | ||
| 157 | c[2] = mem.readIntLE(u32, nonce[0..4]); | ||
| 158 | c[3] = mem.readIntLE(u32, nonce[4..8]); | ||
| 159 | |||
| 160 | const block_size = (1 << 6); | ||
| 161 | const big_block = (block_size << 32); | ||
| 162 | |||
| 163 | // first partial big block | ||
| 164 | if (((@intCast(u64, @maxValue(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) { | ||
| 165 | chaCha20_internal(out[cursor..big_block], in[cursor..big_block], k, c); | ||
| 166 | cursor = big_block - cursor; | ||
| 167 | c[1] += 1; | ||
| 168 | if (comptime @sizeOf(usize) > 4) { | ||
| 169 | // A big block is giant: 256 GiB, but we can avoid this limitation | ||
| 170 | var remaining_blocks: u32 = @intCast(u32, (in.len / big_block)); | ||
| 171 | var i: u32 = 0; | ||
| 172 | while (remaining_blocks > 0) : (remaining_blocks -= 1) { | ||
| 173 | chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c); | ||
| 174 | c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this. | ||
| 175 | cursor += big_block; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | chaCha20_internal(out[cursor..], in[cursor..], k, c); | ||
| 181 | } | ||
| 182 | |||
| 183 | // https://tools.ietf.org/html/rfc7539#section-2.4.2 | ||
| 184 | test "crypto.chacha20 test vector sunscreen" { | ||
| 185 | const expected_result = []u8{ | ||
| 186 | 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, | ||
| 187 | 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, | ||
| 188 | 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, | ||
| 189 | 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b, | ||
| 190 | 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, | ||
| 191 | 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57, | ||
| 192 | 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, | ||
| 193 | 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8, | ||
| 194 | 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, | ||
| 195 | 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e, | ||
| 196 | 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, | ||
| 197 | 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36, | ||
| 198 | 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, | ||
| 199 | 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42, | ||
| 200 | 0x87, 0x4d, | ||
| 201 | }; | ||
| 202 | const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; | ||
| 203 | var result: [114]u8 = undefined; | ||
| 204 | const key = []u8{ | ||
| 205 | 0, 1, 2, 3, 4, 5, 6, 7, | ||
| 206 | 8, 9, 10, 11, 12, 13, 14, 15, | ||
| 207 | 16, 17, 18, 19, 20, 21, 22, 23, | ||
| 208 | 24, 25, 26, 27, 28, 29, 30, 31, | ||
| 209 | }; | ||
| 210 | const nonce = []u8{ | ||
| 211 | 0, 0, 0, 0, | ||
| 212 | 0, 0, 0, 0x4a, | ||
| 213 | 0, 0, 0, 0, | ||
| 214 | }; | ||
| 215 | |||
| 216 | chaCha20IETF(result[0..], input[0..], 1, key, nonce); | ||
| 217 | assert(mem.eql(u8, expected_result, result)); | ||
| 218 | |||
| 219 | // Chacha20 is self-reversing. | ||
| 220 | var plaintext: [114]u8 = undefined; | ||
| 221 | chaCha20IETF(plaintext[0..], result[0..], 1, key, nonce); | ||
| 222 | assert(mem.compare(u8, input, plaintext) == mem.Compare.Equal); | ||
| 223 | } | ||
| 224 | |||
| 225 | // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 | ||
| 226 | test "crypto.chacha20 test vector 1" { | ||
| 227 | const expected_result = []u8{ | ||
| 228 | 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, | ||
| 229 | 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, | ||
| 230 | 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, | ||
| 231 | 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, | ||
| 232 | 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, | ||
| 233 | 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, | ||
| 234 | 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, | ||
| 235 | 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86, | ||
| 236 | }; | ||
| 237 | const input = []u8{ | ||
| 238 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 239 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 240 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 241 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 242 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 243 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 244 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 245 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 246 | }; | ||
| 247 | var result: [64]u8 = undefined; | ||
| 248 | const key = []u8{ | ||
| 249 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 250 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 251 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 252 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 253 | }; | ||
| 254 | const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
| 255 | |||
| 256 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); | ||
| 257 | assert(mem.eql(u8, expected_result, result)); | ||
| 258 | } | ||
| 259 | |||
| 260 | test "crypto.chacha20 test vector 2" { | ||
| 261 | const expected_result = []u8{ | ||
| 262 | 0x45, 0x40, 0xf0, 0x5a, 0x9f, 0x1f, 0xb2, 0x96, | ||
| 263 | 0xd7, 0x73, 0x6e, 0x7b, 0x20, 0x8e, 0x3c, 0x96, | ||
| 264 | 0xeb, 0x4f, 0xe1, 0x83, 0x46, 0x88, 0xd2, 0x60, | ||
| 265 | 0x4f, 0x45, 0x09, 0x52, 0xed, 0x43, 0x2d, 0x41, | ||
| 266 | 0xbb, 0xe2, 0xa0, 0xb6, 0xea, 0x75, 0x66, 0xd2, | ||
| 267 | 0xa5, 0xd1, 0xe7, 0xe2, 0x0d, 0x42, 0xaf, 0x2c, | ||
| 268 | 0x53, 0xd7, 0x92, 0xb1, 0xc4, 0x3f, 0xea, 0x81, | ||
| 269 | 0x7e, 0x9a, 0xd2, 0x75, 0xae, 0x54, 0x69, 0x63, | ||
| 270 | }; | ||
| 271 | const input = []u8{ | ||
| 272 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 273 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 274 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 275 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 276 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 277 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 278 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 279 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 280 | }; | ||
| 281 | var result: [64]u8 = undefined; | ||
| 282 | const key = []u8{ | ||
| 283 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 284 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 285 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 286 | 0, 0, 0, 0, 0, 0, 0, 1, | ||
| 287 | }; | ||
| 288 | const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
| 289 | |||
| 290 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); | ||
| 291 | assert(mem.eql(u8, expected_result, result)); | ||
| 292 | } | ||
| 293 | |||
| 294 | test "crypto.chacha20 test vector 3" { | ||
| 295 | const expected_result = []u8{ | ||
| 296 | 0xde, 0x9c, 0xba, 0x7b, 0xf3, 0xd6, 0x9e, 0xf5, | ||
| 297 | 0xe7, 0x86, 0xdc, 0x63, 0x97, 0x3f, 0x65, 0x3a, | ||
| 298 | 0x0b, 0x49, 0xe0, 0x15, 0xad, 0xbf, 0xf7, 0x13, | ||
| 299 | 0x4f, 0xcb, 0x7d, 0xf1, 0x37, 0x82, 0x10, 0x31, | ||
| 300 | 0xe8, 0x5a, 0x05, 0x02, 0x78, 0xa7, 0x08, 0x45, | ||
| 301 | 0x27, 0x21, 0x4f, 0x73, 0xef, 0xc7, 0xfa, 0x5b, | ||
| 302 | 0x52, 0x77, 0x06, 0x2e, 0xb7, 0xa0, 0x43, 0x3e, | ||
| 303 | 0x44, 0x5f, 0x41, 0xe3, | ||
| 304 | }; | ||
| 305 | const input = []u8{ | ||
| 306 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 307 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 308 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 309 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 310 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 311 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 312 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 313 | 0x00, 0x00, 0x00, 0x00, | ||
| 314 | }; | ||
| 315 | var result: [60]u8 = undefined; | ||
| 316 | const key = []u8{ | ||
| 317 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 318 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 319 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 320 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 321 | }; | ||
| 322 | const nonce = []u8{ 0, 0, 0, 0, 0, 0, 0, 1 }; | ||
| 323 | |||
| 324 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); | ||
| 325 | assert(mem.eql(u8, expected_result, result)); | ||
| 326 | } | ||
| 327 | |||
| 328 | test "crypto.chacha20 test vector 4" { | ||
| 329 | const expected_result = []u8{ | ||
| 330 | 0xef, 0x3f, 0xdf, 0xd6, 0xc6, 0x15, 0x78, 0xfb, | ||
| 331 | 0xf5, 0xcf, 0x35, 0xbd, 0x3d, 0xd3, 0x3b, 0x80, | ||
| 332 | 0x09, 0x63, 0x16, 0x34, 0xd2, 0x1e, 0x42, 0xac, | ||
| 333 | 0x33, 0x96, 0x0b, 0xd1, 0x38, 0xe5, 0x0d, 0x32, | ||
| 334 | 0x11, 0x1e, 0x4c, 0xaf, 0x23, 0x7e, 0xe5, 0x3c, | ||
| 335 | 0xa8, 0xad, 0x64, 0x26, 0x19, 0x4a, 0x88, 0x54, | ||
| 336 | 0x5d, 0xdc, 0x49, 0x7a, 0x0b, 0x46, 0x6e, 0x7d, | ||
| 337 | 0x6b, 0xbd, 0xb0, 0x04, 0x1b, 0x2f, 0x58, 0x6b, | ||
| 338 | }; | ||
| 339 | const input = []u8{ | ||
| 340 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 341 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 342 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 343 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 344 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 345 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 346 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 347 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
| 348 | }; | ||
| 349 | var result: [64]u8 = undefined; | ||
| 350 | const key = []u8{ | ||
| 351 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 352 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 353 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 354 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 355 | }; | ||
| 356 | const nonce = []u8{ 1, 0, 0, 0, 0, 0, 0, 0 }; | ||
| 357 | |||
| 358 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); | ||
| 359 | assert(mem.eql(u8, expected_result, result)); | ||
| 360 | } | ||
| 361 | |||
| 362 | test "crypto.chacha20 test vector 5" { | ||
| 363 | const expected_result = []u8{ | ||
| 364 | 0xf7, 0x98, 0xa1, 0x89, 0xf1, 0x95, 0xe6, 0x69, | ||
| 365 | 0x82, 0x10, 0x5f, 0xfb, 0x64, 0x0b, 0xb7, 0x75, | ||
| 366 | 0x7f, 0x57, 0x9d, 0xa3, 0x16, 0x02, 0xfc, 0x93, | ||
| 367 | 0xec, 0x01, 0xac, 0x56, 0xf8, 0x5a, 0xc3, 0xc1, | ||
| 368 | 0x34, 0xa4, 0x54, 0x7b, 0x73, 0x3b, 0x46, 0x41, | ||
| 369 | 0x30, 0x42, 0xc9, 0x44, 0x00, 0x49, 0x17, 0x69, | ||
| 370 | 0x05, 0xd3, 0xbe, 0x59, 0xea, 0x1c, 0x53, 0xf1, | ||
| 371 | 0x59, 0x16, 0x15, 0x5c, 0x2b, 0xe8, 0x24, 0x1a, | ||
| 372 | |||
| 373 | 0x38, 0x00, 0x8b, 0x9a, 0x26, 0xbc, 0x35, 0x94, | ||
| 374 | 0x1e, 0x24, 0x44, 0x17, 0x7c, 0x8a, 0xde, 0x66, | ||
| 375 | 0x89, 0xde, 0x95, 0x26, 0x49, 0x86, 0xd9, 0x58, | ||
| 376 | 0x89, 0xfb, 0x60, 0xe8, 0x46, 0x29, 0xc9, 0xbd, | ||
| 377 | 0x9a, 0x5a, 0xcb, 0x1c, 0xc1, 0x18, 0xbe, 0x56, | ||
| 378 | 0x3e, 0xb9, 0xb3, 0xa4, 0xa4, 0x72, 0xf8, 0x2e, | ||
| 379 | 0x09, 0xa7, 0xe7, 0x78, 0x49, 0x2b, 0x56, 0x2e, | ||
| 380 | 0xf7, 0x13, 0x0e, 0x88, 0xdf, 0xe0, 0x31, 0xc7, | ||
| 381 | |||
| 382 | 0x9d, 0xb9, 0xd4, 0xf7, 0xc7, 0xa8, 0x99, 0x15, | ||
| 383 | 0x1b, 0x9a, 0x47, 0x50, 0x32, 0xb6, 0x3f, 0xc3, | ||
| 384 | 0x85, 0x24, 0x5f, 0xe0, 0x54, 0xe3, 0xdd, 0x5a, | ||
| 385 | 0x97, 0xa5, 0xf5, 0x76, 0xfe, 0x06, 0x40, 0x25, | ||
| 386 | 0xd3, 0xce, 0x04, 0x2c, 0x56, 0x6a, 0xb2, 0xc5, | ||
| 387 | 0x07, 0xb1, 0x38, 0xdb, 0x85, 0x3e, 0x3d, 0x69, | ||
| 388 | 0x59, 0x66, 0x09, 0x96, 0x54, 0x6c, 0xc9, 0xc4, | ||
| 389 | 0xa6, 0xea, 0xfd, 0xc7, 0x77, 0xc0, 0x40, 0xd7, | ||
| 390 | |||
| 391 | 0x0e, 0xaf, 0x46, 0xf7, 0x6d, 0xad, 0x39, 0x79, | ||
| 392 | 0xe5, 0xc5, 0x36, 0x0c, 0x33, 0x17, 0x16, 0x6a, | ||
| 393 | 0x1c, 0x89, 0x4c, 0x94, 0xa3, 0x71, 0x87, 0x6a, | ||
| 394 | 0x94, 0xdf, 0x76, 0x28, 0xfe, 0x4e, 0xaa, 0xf2, | ||
| 395 | 0xcc, 0xb2, 0x7d, 0x5a, 0xaa, 0xe0, 0xad, 0x7a, | ||
| 396 | 0xd0, 0xf9, 0xd4, 0xb6, 0xad, 0x3b, 0x54, 0x09, | ||
| 397 | 0x87, 0x46, 0xd4, 0x52, 0x4d, 0x38, 0x40, 0x7a, | ||
| 398 | 0x6d, 0xeb, 0x3a, 0xb7, 0x8f, 0xab, 0x78, 0xc9, | ||
| 399 | }; | ||
| 400 | const input = []u8{ | ||
| 401 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 402 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 403 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 404 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 405 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 406 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 407 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 408 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 409 | |||
| 410 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 411 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 412 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 413 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 414 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 415 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 416 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 417 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
| 418 | }; | ||
| 419 | var result: [256]u8 = undefined; | ||
| 420 | const key = []u8{ | ||
| 421 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | ||
| 422 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, | ||
| 423 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, | ||
| 424 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, | ||
| 425 | }; | ||
| 426 | const nonce = []u8{ | ||
| 427 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | ||
| 428 | }; | ||
| 429 | |||
| 430 | chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce); | ||
| 431 | assert(mem.eql(u8, expected_result, result)); | ||
| 432 | } | ||
std/crypto/hmac.zig+54-37| ... | @@ -7,46 +7,63 @@ pub const HmacMd5 = Hmac(crypto.Md5); | ... | @@ -7,46 +7,63 @@ pub const HmacMd5 = Hmac(crypto.Md5); |
| 7 | pub const HmacSha1 = Hmac(crypto.Sha1); | 7 | pub const HmacSha1 = Hmac(crypto.Sha1); |
| 8 | pub const HmacSha256 = Hmac(crypto.Sha256); | 8 | pub const HmacSha256 = Hmac(crypto.Sha256); |
| 9 | 9 | ||
| 10 | pub fn Hmac(comptime H: type) type { | 10 | pub fn Hmac(comptime Hash: type) type { |
| 11 | return struct { | 11 | return struct { |
| 12 | const digest_size = H.digest_size; | 12 | const Self = this; |
| 13 | pub const mac_length = Hash.digest_length; | ||
| 14 | pub const minimum_key_length = 0; | ||
| 13 | 15 | ||
| 14 | pub fn hash(output: []u8, key: []const u8, message: []const u8) void { | 16 | o_key_pad: [Hash.block_length]u8, |
| 15 | debug.assert(output.len >= H.digest_size); | 17 | i_key_pad: [Hash.block_length]u8, |
| 16 | debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption | 18 | scratch: [Hash.block_length]u8, |
| 17 | var scratch: [H.block_size]u8 = undefined; | 19 | hash: Hash, |
| 20 | |||
| 21 | // HMAC(k, m) = H(o_key_pad | H(i_key_pad | msg)) where | is concatenation | ||
| 22 | pub fn create(out: []u8, msg: []const u8, key: []const u8) void { | ||
| 23 | var ctx = Self.init(key); | ||
| 24 | ctx.update(msg); | ||
| 25 | ctx.final(out[0..]); | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn init(key: []const u8) Self { | ||
| 29 | var ctx: Self = undefined; | ||
| 18 | 30 | ||
| 19 | // Normalize key length to block size of hash | 31 | // Normalize key length to block size of hash |
| 20 | if (key.len > H.block_size) { | 32 | if (key.len > Hash.block_length) { |
| 21 | H.hash(key, scratch[0..H.digest_size]); | 33 | Hash.hash(key, ctx.scratch[0..mac_length]); |
| 22 | mem.set(u8, scratch[H.digest_size..H.block_size], 0); | 34 | mem.set(u8, ctx.scratch[mac_length..Hash.block_length], 0); |
| 23 | } else if (key.len < H.block_size) { | 35 | } else if (key.len < Hash.block_length) { |
| 24 | mem.copy(u8, scratch[0..key.len], key); | 36 | mem.copy(u8, ctx.scratch[0..key.len], key); |
| 25 | mem.set(u8, scratch[key.len..H.block_size], 0); | 37 | mem.set(u8, ctx.scratch[key.len..Hash.block_length], 0); |
| 26 | } else { | 38 | } else { |
| 27 | mem.copy(u8, scratch[0..], key); | 39 | mem.copy(u8, ctx.scratch[0..], key); |
| 28 | } | 40 | } |
| 29 | 41 | ||
| 30 | var o_key_pad: [H.block_size]u8 = undefined; | 42 | for (ctx.o_key_pad) |*b, i| { |
| 31 | for (o_key_pad) |*b, i| { | 43 | b.* = ctx.scratch[i] ^ 0x5c; |
| 32 | b.* = scratch[i] ^ 0x5c; | ||
| 33 | } | 44 | } |
| 34 | 45 | ||
| 35 | var i_key_pad: [H.block_size]u8 = undefined; | 46 | for (ctx.i_key_pad) |*b, i| { |
| 36 | for (i_key_pad) |*b, i| { | 47 | b.* = ctx.scratch[i] ^ 0x36; |
| 37 | b.* = scratch[i] ^ 0x36; | ||
| 38 | } | 48 | } |
| 39 | 49 | ||
| 40 | // HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation | 50 | ctx.hash = Hash.init(); |
| 41 | var hmac = H.init(); | 51 | ctx.hash.update(ctx.i_key_pad[0..]); |
| 42 | hmac.update(i_key_pad[0..]); | 52 | return ctx; |
| 43 | hmac.update(message); | 53 | } |
| 44 | hmac.final(scratch[0..H.digest_size]); | 54 | |
| 55 | pub fn update(ctx: *Self, msg: []const u8) void { | ||
| 56 | ctx.hash.update(msg); | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn final(ctx: *Self, out: []u8) void { | ||
| 60 | debug.assert(Hash.block_length >= out.len and out.len >= mac_length); | ||
| 45 | 61 | ||
| 46 | hmac.reset(); | 62 | ctx.hash.final(ctx.scratch[0..mac_length]); |
| 47 | hmac.update(o_key_pad[0..]); | 63 | ctx.hash.reset(); |
| 48 | hmac.update(scratch[0..H.digest_size]); | 64 | ctx.hash.update(ctx.o_key_pad[0..]); |
| 49 | hmac.final(output[0..H.digest_size]); | 65 | ctx.hash.update(ctx.scratch[0..mac_length]); |
| 66 | ctx.hash.final(out[0..mac_length]); | ||
| 50 | } | 67 | } |
| 51 | }; | 68 | }; |
| 52 | } | 69 | } |
| ... | @@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type { | ... | @@ -54,28 +71,28 @@ pub fn Hmac(comptime H: type) type { |
| 54 | const htest = @import("test.zig"); | 71 | const htest = @import("test.zig"); |
| 55 | 72 | ||
| 56 | test "hmac md5" { | 73 | test "hmac md5" { |
| 57 | var out: [crypto.Md5.digest_size]u8 = undefined; | 74 | var out: [HmacMd5.mac_length]u8 = undefined; |
| 58 | HmacMd5.hash(out[0..], "", ""); | 75 | HmacMd5.create(out[0..], "", ""); |
| 59 | htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]); | 76 | htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]); |
| 60 | 77 | ||
| 61 | HmacMd5.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog"); | 78 | HmacMd5.create(out[0..], "The quick brown fox jumps over the lazy dog", "key"); |
| 62 | htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]); | 79 | htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]); |
| 63 | } | 80 | } |
| 64 | 81 | ||
| 65 | test "hmac sha1" { | 82 | test "hmac sha1" { |
| 66 | var out: [crypto.Sha1.digest_size]u8 = undefined; | 83 | var out: [HmacSha1.mac_length]u8 = undefined; |
| 67 | HmacSha1.hash(out[0..], "", ""); | 84 | HmacSha1.create(out[0..], "", ""); |
| 68 | htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]); | 85 | htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]); |
| 69 | 86 | ||
| 70 | HmacSha1.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog"); | 87 | HmacSha1.create(out[0..], "The quick brown fox jumps over the lazy dog", "key"); |
| 71 | htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]); | 88 | htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]); |
| 72 | } | 89 | } |
| 73 | 90 | ||
| 74 | test "hmac sha256" { | 91 | test "hmac sha256" { |
| 75 | var out: [crypto.Sha256.digest_size]u8 = undefined; | 92 | var out: [HmacSha256.mac_length]u8 = undefined; |
| 76 | HmacSha256.hash(out[0..], "", ""); | 93 | HmacSha256.create(out[0..], "", ""); |
| 77 | htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]); | 94 | htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]); |
| 78 | 95 | ||
| 79 | HmacSha256.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog"); | 96 | HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key"); |
| 80 | htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]); | 97 | htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]); |
| 81 | } | 98 | } |
std/crypto/index.zig+14-4| ... | @@ -21,14 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512; | ... | @@ -21,14 +21,24 @@ pub const Blake2b512 = blake2.Blake2b512; |
| 21 | 21 | ||
| 22 | const hmac = @import("hmac.zig"); | 22 | const hmac = @import("hmac.zig"); |
| 23 | pub const HmacMd5 = hmac.HmacMd5; | 23 | pub const HmacMd5 = hmac.HmacMd5; |
| 24 | pub const HmacSha1 = hmac.Sha1; | 24 | pub const HmacSha1 = hmac.HmacSha1; |
| 25 | pub const HmacSha256 = hmac.Sha256; | 25 | pub const HmacSha256 = hmac.HmacSha256; |
| 26 | |||
| 27 | const import_chaCha20 = @import("chacha20.zig"); | ||
| 28 | pub const chaCha20IETF = import_chaCha20.chaCha20IETF; | ||
| 29 | pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; | ||
| 30 | |||
| 31 | pub const Poly1305 = @import("poly1305.zig").Poly1305; | ||
| 32 | pub const X25519 = @import("x25519.zig").X25519; | ||
| 26 | 33 | ||
| 27 | test "crypto" { | 34 | test "crypto" { |
| 35 | _ = @import("blake2.zig"); | ||
| 36 | _ = @import("chacha20.zig"); | ||
| 37 | _ = @import("hmac.zig"); | ||
| 28 | _ = @import("md5.zig"); | 38 | _ = @import("md5.zig"); |
| 39 | _ = @import("poly1305.zig"); | ||
| 29 | _ = @import("sha1.zig"); | 40 | _ = @import("sha1.zig"); |
| 30 | _ = @import("sha2.zig"); | 41 | _ = @import("sha2.zig"); |
| 31 | _ = @import("sha3.zig"); | 42 | _ = @import("sha3.zig"); |
| 32 | _ = @import("blake2.zig"); | 43 | _ = @import("x25519.zig"); |
| 33 | _ = @import("hmac.zig"); | ||
| 34 | } | 44 | } |
std/crypto/md5.zig+4-4| ... | @@ -29,8 +29,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar | ... | @@ -29,8 +29,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar |
| 29 | 29 | ||
| 30 | pub const Md5 = struct { | 30 | pub const Md5 = struct { |
| 31 | const Self = this; | 31 | const Self = this; |
| 32 | const block_size = 64; | 32 | const block_length = 64; |
| 33 | const digest_size = 16; | 33 | const digest_length = 16; |
| 34 | 34 | ||
| 35 | s: [4]u32, | 35 | s: [4]u32, |
| 36 | // Streaming Cache | 36 | // Streaming Cache |
| ... | @@ -271,8 +271,8 @@ test "md5 streaming" { | ... | @@ -271,8 +271,8 @@ test "md5 streaming" { |
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | test "md5 aligned final" { | 273 | test "md5 aligned final" { |
| 274 | var block = []u8{0} ** Md5.block_size; | 274 | var block = []u8{0} ** Md5.block_length; |
| 275 | var out: [Md5.digest_size]u8 = undefined; | 275 | var out: [Md5.digest_length]u8 = undefined; |
| 276 | 276 | ||
| 277 | var h = Md5.init(); | 277 | var h = Md5.init(); |
| 278 | h.update(block); | 278 | h.update(block); |
std/crypto/poly1305.zig created+233| ... | @@ -0,0 +1,233 @@ | ||
| 1 | // Translated from monocypher which is licensed under CC-0/BSD-3. | ||
| 2 | // | ||
| 3 | // https://monocypher.org/ | ||
| 4 | |||
| 5 | const std = @import("../index.zig"); | ||
| 6 | const builtin = @import("builtin"); | ||
| 7 | |||
| 8 | const Endian = builtin.Endian; | ||
| 9 | const readInt = std.mem.readInt; | ||
| 10 | const writeInt = std.mem.writeInt; | ||
| 11 | |||
| 12 | pub const Poly1305 = struct { | ||
| 13 | const Self = this; | ||
| 14 | |||
| 15 | pub const mac_length = 16; | ||
| 16 | pub const minimum_key_length = 32; | ||
| 17 | |||
| 18 | // constant multiplier (from the secret key) | ||
| 19 | r: [4]u32, | ||
| 20 | // accumulated hash | ||
| 21 | h: [5]u32, | ||
| 22 | // chunk of the message | ||
| 23 | c: [5]u32, | ||
| 24 | // random number added at the end (from the secret key) | ||
| 25 | pad: [4]u32, | ||
| 26 | // How many bytes are there in the chunk. | ||
| 27 | c_idx: usize, | ||
| 28 | |||
| 29 | fn secureZero(self: *Self) void { | ||
| 30 | std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Poly1305)]); | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn create(out: []u8, msg: []const u8, key: []const u8) void { | ||
| 34 | std.debug.assert(out.len >= mac_length); | ||
| 35 | std.debug.assert(key.len >= minimum_key_length); | ||
| 36 | |||
| 37 | var ctx = Poly1305.init(key); | ||
| 38 | ctx.update(msg); | ||
| 39 | ctx.final(out); | ||
| 40 | } | ||
| 41 | |||
| 42 | // Initialize the MAC context. | ||
| 43 | // - key.len is sufficient size. | ||
| 44 | pub fn init(key: []const u8) Self { | ||
| 45 | var ctx: Poly1305 = undefined; | ||
| 46 | |||
| 47 | // Initial hash is zero | ||
| 48 | { | ||
| 49 | var i: usize = 0; | ||
| 50 | while (i < 5) : (i += 1) { | ||
| 51 | ctx.h[i] = 0; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | // add 2^130 to every input block | ||
| 55 | ctx.c[4] = 1; | ||
| 56 | polyClearC(&ctx); | ||
| 57 | |||
| 58 | // load r and pad (r has some of its bits cleared) | ||
| 59 | { | ||
| 60 | var i: usize = 0; | ||
| 61 | while (i < 1) : (i += 1) { | ||
| 62 | ctx.r[0] = readInt(key[0..4], u32, Endian.Little) & 0x0fffffff; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | { | ||
| 66 | var i: usize = 1; | ||
| 67 | while (i < 4) : (i += 1) { | ||
| 68 | ctx.r[i] = readInt(key[i * 4 .. i * 4 + 4], u32, Endian.Little) & 0x0ffffffc; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | { | ||
| 72 | var i: usize = 0; | ||
| 73 | while (i < 4) : (i += 1) { | ||
| 74 | ctx.pad[i] = readInt(key[i * 4 + 16 .. i * 4 + 16 + 4], u32, Endian.Little); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | return ctx; | ||
| 79 | } | ||
| 80 | |||
| 81 | // h = (h + c) * r | ||
| 82 | // preconditions: | ||
| 83 | // ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff | ||
| 84 | // ctx->c <= 1_ffffffff_ffffffff_ffffffff_ffffffff | ||
| 85 | // ctx->r <= 0ffffffc_0ffffffc_0ffffffc_0fffffff | ||
| 86 | // Postcondition: | ||
| 87 | // ctx->h <= 4_ffffffff_ffffffff_ffffffff_ffffffff | ||
| 88 | fn polyBlock(ctx: *Self) void { | ||
| 89 | // s = h + c, without carry propagation | ||
| 90 | const s0 = u64(ctx.h[0]) + ctx.c[0]; // s0 <= 1_fffffffe | ||
| 91 | const s1 = u64(ctx.h[1]) + ctx.c[1]; // s1 <= 1_fffffffe | ||
| 92 | const s2 = u64(ctx.h[2]) + ctx.c[2]; // s2 <= 1_fffffffe | ||
| 93 | const s3 = u64(ctx.h[3]) + ctx.c[3]; // s3 <= 1_fffffffe | ||
| 94 | const s4 = u64(ctx.h[4]) + ctx.c[4]; // s4 <= 5 | ||
| 95 | |||
| 96 | // Local all the things! | ||
| 97 | const r0 = ctx.r[0]; // r0 <= 0fffffff | ||
| 98 | const r1 = ctx.r[1]; // r1 <= 0ffffffc | ||
| 99 | const r2 = ctx.r[2]; // r2 <= 0ffffffc | ||
| 100 | const r3 = ctx.r[3]; // r3 <= 0ffffffc | ||
| 101 | const rr0 = (r0 >> 2) * 5; // rr0 <= 13fffffb // lose 2 bits... | ||
| 102 | const rr1 = (r1 >> 2) + r1; // rr1 <= 13fffffb // rr1 == (r1 >> 2) * 5 | ||
| 103 | const rr2 = (r2 >> 2) + r2; // rr2 <= 13fffffb // rr1 == (r2 >> 2) * 5 | ||
| 104 | const rr3 = (r3 >> 2) + r3; // rr3 <= 13fffffb // rr1 == (r3 >> 2) * 5 | ||
| 105 | |||
| 106 | // (h + c) * r, without carry propagation | ||
| 107 | const x0 = s0 * r0 + s1 * rr3 + s2 * rr2 + s3 * rr1 + s4 * rr0; //<=97ffffe007fffff8 | ||
| 108 | const x1 = s0 * r1 + s1 * r0 + s2 * rr3 + s3 * rr2 + s4 * rr1; //<=8fffffe20ffffff6 | ||
| 109 | const x2 = s0 * r2 + s1 * r1 + s2 * r0 + s3 * rr3 + s4 * rr2; //<=87ffffe417fffff4 | ||
| 110 | const x3 = s0 * r3 + s1 * r2 + s2 * r1 + s3 * r0 + s4 * rr3; //<=7fffffe61ffffff2 | ||
| 111 | const x4 = s4 * (r0 & 3); // ...recover 2 bits //<= f | ||
| 112 | |||
| 113 | // partial reduction modulo 2^130 - 5 | ||
| 114 | const _u5 = @truncate(u32, x4 + (x3 >> 32)); // u5 <= 7ffffff5 | ||
| 115 | const _u0 = (_u5 >> 2) * 5 + (x0 & 0xffffffff); | ||
| 116 | const _u1 = (_u0 >> 32) + (x1 & 0xffffffff) + (x0 >> 32); | ||
| 117 | const _u2 = (_u1 >> 32) + (x2 & 0xffffffff) + (x1 >> 32); | ||
| 118 | const _u3 = (_u2 >> 32) + (x3 & 0xffffffff) + (x2 >> 32); | ||
| 119 | const _u4 = (_u3 >> 32) + (_u5 & 3); | ||
| 120 | |||
| 121 | // Update the hash | ||
| 122 | ctx.h[0] = @truncate(u32, _u0); // u0 <= 1_9ffffff0 | ||
| 123 | ctx.h[1] = @truncate(u32, _u1); // u1 <= 1_97ffffe0 | ||
| 124 | ctx.h[2] = @truncate(u32, _u2); // u2 <= 1_8fffffe2 | ||
| 125 | ctx.h[3] = @truncate(u32, _u3); // u3 <= 1_87ffffe4 | ||
| 126 | ctx.h[4] = @truncate(u32, _u4); // u4 <= 4 | ||
| 127 | } | ||
| 128 | |||
| 129 | // (re-)initializes the input counter and input buffer | ||
| 130 | fn polyClearC(ctx: *Self) void { | ||
| 131 | ctx.c[0] = 0; | ||
| 132 | ctx.c[1] = 0; | ||
| 133 | ctx.c[2] = 0; | ||
| 134 | ctx.c[3] = 0; | ||
| 135 | ctx.c_idx = 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | fn polyTakeInput(ctx: *Self, input: u8) void { | ||
| 139 | const word = ctx.c_idx >> 2; | ||
| 140 | const byte = ctx.c_idx & 3; | ||
| 141 | ctx.c[word] |= std.math.shl(u32, input, byte * 8); | ||
| 142 | ctx.c_idx += 1; | ||
| 143 | } | ||
| 144 | |||
| 145 | fn polyUpdate(ctx: *Self, msg: []const u8) void { | ||
| 146 | for (msg) |b| { | ||
| 147 | polyTakeInput(ctx, b); | ||
| 148 | if (ctx.c_idx == 16) { | ||
| 149 | polyBlock(ctx); | ||
| 150 | polyClearC(ctx); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | fn alignTo(x: usize, block_size: usize) usize { | ||
| 156 | return ((~x) +% 1) & (block_size - 1); | ||
| 157 | } | ||
| 158 | |||
| 159 | // Feed data into the MAC context. | ||
| 160 | pub fn update(ctx: *Self, msg: []const u8) void { | ||
| 161 | // Align ourselves with block boundaries | ||
| 162 | const alignm = std.math.min(alignTo(ctx.c_idx, 16), msg.len); | ||
| 163 | polyUpdate(ctx, msg[0..alignm]); | ||
| 164 | |||
| 165 | var nmsg = msg[alignm..]; | ||
| 166 | |||
| 167 | // Process the msg block by block | ||
| 168 | const nb_blocks = nmsg.len >> 4; | ||
| 169 | var i: usize = 0; | ||
| 170 | while (i < nb_blocks) : (i += 1) { | ||
| 171 | ctx.c[0] = readInt(nmsg[0..4], u32, Endian.Little); | ||
| 172 | ctx.c[1] = readInt(nmsg[4..8], u32, Endian.Little); | ||
| 173 | ctx.c[2] = readInt(nmsg[8..12], u32, Endian.Little); | ||
| 174 | ctx.c[3] = readInt(nmsg[12..16], u32, Endian.Little); | ||
| 175 | polyBlock(ctx); | ||
| 176 | nmsg = nmsg[16..]; | ||
| 177 | } | ||
| 178 | if (nb_blocks > 0) { | ||
| 179 | polyClearC(ctx); | ||
| 180 | } | ||
| 181 | |||
| 182 | // remaining bytes | ||
| 183 | polyUpdate(ctx, nmsg[0..]); | ||
| 184 | } | ||
| 185 | |||
| 186 | // Finalize the MAC and output into buffer provided by caller. | ||
| 187 | pub fn final(ctx: *Self, out: []u8) void { | ||
| 188 | // Process the last block (if any) | ||
| 189 | if (ctx.c_idx != 0) { | ||
| 190 | // move the final 1 according to remaining input length | ||
| 191 | // (We may add less than 2^130 to the last input block) | ||
| 192 | ctx.c[4] = 0; | ||
| 193 | polyTakeInput(ctx, 1); | ||
| 194 | // one last hash update | ||
| 195 | polyBlock(ctx); | ||
| 196 | } | ||
| 197 | |||
| 198 | // check if we should subtract 2^130-5 by performing the | ||
| 199 | // corresponding carry propagation. | ||
| 200 | const _u0 = u64(5) + ctx.h[0]; // <= 1_00000004 | ||
| 201 | const _u1 = (_u0 >> 32) + ctx.h[1]; // <= 1_00000000 | ||
| 202 | const _u2 = (_u1 >> 32) + ctx.h[2]; // <= 1_00000000 | ||
| 203 | const _u3 = (_u2 >> 32) + ctx.h[3]; // <= 1_00000000 | ||
| 204 | const _u4 = (_u3 >> 32) + ctx.h[4]; // <= 5 | ||
| 205 | // u4 indicates how many times we should subtract 2^130-5 (0 or 1) | ||
| 206 | |||
| 207 | // h + pad, minus 2^130-5 if u4 exceeds 3 | ||
| 208 | const uu0 = (_u4 >> 2) * 5 + ctx.h[0] + ctx.pad[0]; // <= 2_00000003 | ||
| 209 | const uu1 = (uu0 >> 32) + ctx.h[1] + ctx.pad[1]; // <= 2_00000000 | ||
| 210 | const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000 | ||
| 211 | const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000 | ||
| 212 | |||
| 213 | writeInt(out[0..], @truncate(u32, uu0), Endian.Little); | ||
| 214 | writeInt(out[4..], @truncate(u32, uu1), Endian.Little); | ||
| 215 | writeInt(out[8..], @truncate(u32, uu2), Endian.Little); | ||
| 216 | writeInt(out[12..], @truncate(u32, uu3), Endian.Little); | ||
| 217 | |||
| 218 | ctx.secureZero(); | ||
| 219 | } | ||
| 220 | }; | ||
| 221 | |||
| 222 | test "poly1305 rfc7439 vector1" { | ||
| 223 | const expected_mac = "\xa8\x06\x1d\xc1\x30\x51\x36\xc6\xc2\x2b\x8b\xaf\x0c\x01\x27\xa9"; | ||
| 224 | |||
| 225 | const msg = "Cryptographic Forum Research Group"; | ||
| 226 | const key = "\x85\xd6\xbe\x78\x57\x55\x6d\x33\x7f\x44\x52\xfe\x42\xd5\x06\xa8" ++ | ||
| 227 | "\x01\x03\x80\x8a\xfb\x0d\xb2\xfd\x4a\xbf\xf6\xaf\x41\x49\xf5\x1b"; | ||
| 228 | |||
| 229 | var mac: [16]u8 = undefined; | ||
| 230 | Poly1305.create(mac[0..], msg, key); | ||
| 231 | |||
| 232 | std.debug.assert(std.mem.eql(u8, mac, expected_mac)); | ||
| 233 | } | ||
std/crypto/sha1.zig+4-4| ... | @@ -26,8 +26,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { | ... | @@ -26,8 +26,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { |
| 26 | 26 | ||
| 27 | pub const Sha1 = struct { | 27 | pub const Sha1 = struct { |
| 28 | const Self = this; | 28 | const Self = this; |
| 29 | const block_size = 64; | 29 | const block_length = 64; |
| 30 | const digest_size = 20; | 30 | const digest_length = 20; |
| 31 | 31 | ||
| 32 | s: [5]u32, | 32 | s: [5]u32, |
| 33 | // Streaming Cache | 33 | // Streaming Cache |
| ... | @@ -292,8 +292,8 @@ test "sha1 streaming" { | ... | @@ -292,8 +292,8 @@ test "sha1 streaming" { |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | test "sha1 aligned final" { | 294 | test "sha1 aligned final" { |
| 295 | var block = []u8{0} ** Sha1.block_size; | 295 | var block = []u8{0} ** Sha1.block_length; |
| 296 | var out: [Sha1.digest_size]u8 = undefined; | 296 | var out: [Sha1.digest_length]u8 = undefined; |
| 297 | 297 | ||
| 298 | var h = Sha1.init(); | 298 | var h = Sha1.init(); |
| 299 | h.update(block); | 299 | h.update(block); |
std/crypto/sha2.zig+8-8| ... | @@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params); | ... | @@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params); |
| 78 | fn Sha2_32(comptime params: Sha2Params32) type { | 78 | fn Sha2_32(comptime params: Sha2Params32) type { |
| 79 | return struct { | 79 | return struct { |
| 80 | const Self = this; | 80 | const Self = this; |
| 81 | const block_size = 64; | 81 | const block_length = 64; |
| 82 | const digest_size = params.out_len / 8; | 82 | const digest_length = params.out_len / 8; |
| 83 | 83 | ||
| 84 | s: [8]u32, | 84 | s: [8]u32, |
| 85 | // Streaming Cache | 85 | // Streaming Cache |
| ... | @@ -338,8 +338,8 @@ test "sha256 streaming" { | ... | @@ -338,8 +338,8 @@ test "sha256 streaming" { |
| 338 | } | 338 | } |
| 339 | 339 | ||
| 340 | test "sha256 aligned final" { | 340 | test "sha256 aligned final" { |
| 341 | var block = []u8{0} ** Sha256.block_size; | 341 | var block = []u8{0} ** Sha256.block_length; |
| 342 | var out: [Sha256.digest_size]u8 = undefined; | 342 | var out: [Sha256.digest_length]u8 = undefined; |
| 343 | 343 | ||
| 344 | var h = Sha256.init(); | 344 | var h = Sha256.init(); |
| 345 | h.update(block); | 345 | h.update(block); |
| ... | @@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params); | ... | @@ -419,8 +419,8 @@ pub const Sha512 = Sha2_64(Sha512Params); |
| 419 | fn Sha2_64(comptime params: Sha2Params64) type { | 419 | fn Sha2_64(comptime params: Sha2Params64) type { |
| 420 | return struct { | 420 | return struct { |
| 421 | const Self = this; | 421 | const Self = this; |
| 422 | const block_size = 128; | 422 | const block_length = 128; |
| 423 | const digest_size = params.out_len / 8; | 423 | const digest_length = params.out_len / 8; |
| 424 | 424 | ||
| 425 | s: [8]u64, | 425 | s: [8]u64, |
| 426 | // Streaming Cache | 426 | // Streaming Cache |
| ... | @@ -715,8 +715,8 @@ test "sha512 streaming" { | ... | @@ -715,8 +715,8 @@ test "sha512 streaming" { |
| 715 | } | 715 | } |
| 716 | 716 | ||
| 717 | test "sha512 aligned final" { | 717 | test "sha512 aligned final" { |
| 718 | var block = []u8{0} ** Sha512.block_size; | 718 | var block = []u8{0} ** Sha512.block_length; |
| 719 | var out: [Sha512.digest_size]u8 = undefined; | 719 | var out: [Sha512.digest_length]u8 = undefined; |
| 720 | 720 | ||
| 721 | var h = Sha512.init(); | 721 | var h = Sha512.init(); |
| 722 | h.update(block); | 722 | h.update(block); |
std/crypto/sha3.zig+15-88| ... | @@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06); | ... | @@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06); |
| 13 | fn Keccak(comptime bits: usize, comptime delim: u8) type { | 13 | fn Keccak(comptime bits: usize, comptime delim: u8) type { |
| 14 | return struct { | 14 | return struct { |
| 15 | const Self = this; | 15 | const Self = this; |
| 16 | const block_size = 200; | 16 | const block_length = 200; |
| 17 | const digest_size = bits / 8; | 17 | const digest_length = bits / 8; |
| 18 | 18 | ||
| 19 | s: [200]u8, | 19 | s: [200]u8, |
| 20 | offset: usize, | 20 | offset: usize, |
| ... | @@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { | ... | @@ -87,97 +87,24 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type { |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | const RC = []const u64{ | 89 | const RC = []const u64{ |
| 90 | 0x0000000000000001, | 90 | 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, |
| 91 | 0x0000000000008082, | 91 | 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, |
| 92 | 0x800000000000808a, | 92 | 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, |
| 93 | 0x8000000080008000, | 93 | 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, |
| 94 | 0x000000000000808b, | 94 | 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, |
| 95 | 0x0000000080000001, | 95 | 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008, |
| 96 | 0x8000000080008081, | ||
| 97 | 0x8000000000008009, | ||
| 98 | 0x000000000000008a, | ||
| 99 | 0x0000000000000088, | ||
| 100 | 0x0000000080008009, | ||
| 101 | 0x000000008000000a, | ||
| 102 | 0x000000008000808b, | ||
| 103 | 0x800000000000008b, | ||
| 104 | 0x8000000000008089, | ||
| 105 | 0x8000000000008003, | ||
| 106 | 0x8000000000008002, | ||
| 107 | 0x8000000000000080, | ||
| 108 | 0x000000000000800a, | ||
| 109 | 0x800000008000000a, | ||
| 110 | 0x8000000080008081, | ||
| 111 | 0x8000000000008080, | ||
| 112 | 0x0000000080000001, | ||
| 113 | 0x8000000080008008, | ||
| 114 | }; | 96 | }; |
| 115 | 97 | ||
| 116 | const ROTC = []const usize{ | 98 | const ROTC = []const usize{ |
| 117 | 1, | 99 | 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44, |
| 118 | 3, | ||
| 119 | 6, | ||
| 120 | 10, | ||
| 121 | 15, | ||
| 122 | 21, | ||
| 123 | 28, | ||
| 124 | 36, | ||
| 125 | 45, | ||
| 126 | 55, | ||
| 127 | 2, | ||
| 128 | 14, | ||
| 129 | 27, | ||
| 130 | 41, | ||
| 131 | 56, | ||
| 132 | 8, | ||
| 133 | 25, | ||
| 134 | 43, | ||
| 135 | 62, | ||
| 136 | 18, | ||
| 137 | 39, | ||
| 138 | 61, | ||
| 139 | 20, | ||
| 140 | 44, | ||
| 141 | }; | 100 | }; |
| 142 | 101 | ||
| 143 | const PIL = []const usize{ | 102 | const PIL = []const usize{ |
| 144 | 10, | 103 | 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1, |
| 145 | 7, | ||
| 146 | 11, | ||
| 147 | 17, | ||
| 148 | 18, | ||
| 149 | 3, | ||
| 150 | 5, | ||
| 151 | 16, | ||
| 152 | 8, | ||
| 153 | 21, | ||
| 154 | 24, | ||
| 155 | 4, | ||
| 156 | 15, | ||
| 157 | 23, | ||
| 158 | 19, | ||
| 159 | 13, | ||
| 160 | 12, | ||
| 161 | 2, | ||
| 162 | 20, | ||
| 163 | 14, | ||
| 164 | 22, | ||
| 165 | 9, | ||
| 166 | 6, | ||
| 167 | 1, | ||
| 168 | }; | 104 | }; |
| 169 | 105 | ||
| 170 | const M5 = []const usize{ | 106 | const M5 = []const usize{ |
| 171 | 0, | 107 | 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, |
| 172 | 1, | ||
| 173 | 2, | ||
| 174 | 3, | ||
| 175 | 4, | ||
| 176 | 0, | ||
| 177 | 1, | ||
| 178 | 2, | ||
| 179 | 3, | ||
| 180 | 4, | ||
| 181 | }; | 108 | }; |
| 182 | 109 | ||
| 183 | fn keccak_f(comptime F: usize, d: []u8) void { | 110 | fn keccak_f(comptime F: usize, d: []u8) void { |
| ... | @@ -297,8 +224,8 @@ test "sha3-256 streaming" { | ... | @@ -297,8 +224,8 @@ test "sha3-256 streaming" { |
| 297 | } | 224 | } |
| 298 | 225 | ||
| 299 | test "sha3-256 aligned final" { | 226 | test "sha3-256 aligned final" { |
| 300 | var block = []u8{0} ** Sha3_256.block_size; | 227 | var block = []u8{0} ** Sha3_256.block_length; |
| 301 | var out: [Sha3_256.digest_size]u8 = undefined; | 228 | var out: [Sha3_256.digest_length]u8 = undefined; |
| 302 | 229 | ||
| 303 | var h = Sha3_256.init(); | 230 | var h = Sha3_256.init(); |
| 304 | h.update(block); | 231 | h.update(block); |
| ... | @@ -368,8 +295,8 @@ test "sha3-512 streaming" { | ... | @@ -368,8 +295,8 @@ test "sha3-512 streaming" { |
| 368 | } | 295 | } |
| 369 | 296 | ||
| 370 | test "sha3-512 aligned final" { | 297 | test "sha3-512 aligned final" { |
| 371 | var block = []u8{0} ** Sha3_512.block_size; | 298 | var block = []u8{0} ** Sha3_512.block_length; |
| 372 | var out: [Sha3_512.digest_size]u8 = undefined; | 299 | var out: [Sha3_512.digest_length]u8 = undefined; |
| 373 | 300 | ||
| 374 | var h = Sha3_512.init(); | 301 | var h = Sha3_512.init(); |
| 375 | h.update(block); | 302 | h.update(block); |
std/crypto/throughput_test.zig+176-21| ... | @@ -1,38 +1,193 @@ | ... | @@ -1,38 +1,193 @@ |
| 1 | // Modify the HashFunction variable to the one wanted to test. | 1 | const builtin = @import("builtin"); |
| 2 | // | ||
| 3 | // ``` | ||
| 4 | // zig build-exe --release-fast throughput_test.zig | ||
| 5 | // ./throughput_test | ||
| 6 | // ``` | ||
| 7 | |||
| 8 | const std = @import("std"); | 2 | const std = @import("std"); |
| 9 | const time = std.os.time; | 3 | const time = std.os.time; |
| 10 | const Timer = time.Timer; | 4 | const Timer = time.Timer; |
| 11 | const HashFunction = @import("md5.zig").Md5; | 5 | const crypto = @import("index.zig"); |
| 12 | 6 | ||
| 13 | const MiB = 1024 * 1024; | 7 | const KiB = 1024; |
| 14 | const BytesToHash = 1024 * MiB; | 8 | const MiB = 1024 * KiB; |
| 15 | 9 | ||
| 16 | pub fn main() !void { | 10 | var prng = std.rand.DefaultPrng.init(0); |
| 17 | var stdout_file = try std.io.getStdOut(); | ||
| 18 | var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); | ||
| 19 | const stdout = &stdout_out_stream.stream; | ||
| 20 | 11 | ||
| 21 | var block: [HashFunction.block_size]u8 = undefined; | 12 | const Crypto = struct { |
| 22 | std.mem.set(u8, block[0..], 0); | 13 | ty: type, |
| 14 | name: []const u8, | ||
| 15 | }; | ||
| 23 | 16 | ||
| 24 | var h = HashFunction.init(); | 17 | const hashes = []Crypto{ |
| 25 | var offset: usize = 0; | 18 | Crypto{ .ty = crypto.Md5, .name = "md5" }, |
| 19 | Crypto{ .ty = crypto.Sha1, .name = "sha1" }, | ||
| 20 | Crypto{ .ty = crypto.Sha256, .name = "sha256" }, | ||
| 21 | Crypto{ .ty = crypto.Sha512, .name = "sha512" }, | ||
| 22 | Crypto{ .ty = crypto.Sha3_256, .name = "sha3-256" }, | ||
| 23 | Crypto{ .ty = crypto.Sha3_512, .name = "sha3-512" }, | ||
| 24 | Crypto{ .ty = crypto.Blake2s256, .name = "blake2s" }, | ||
| 25 | Crypto{ .ty = crypto.Blake2b512, .name = "blake2b" }, | ||
| 26 | }; | ||
| 27 | |||
| 28 | pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 { | ||
| 29 | var h = Hash.init(); | ||
| 30 | |||
| 31 | var block: [Hash.digest_length]u8 = undefined; | ||
| 32 | prng.random.bytes(block[0..]); | ||
| 26 | 33 | ||
| 34 | var offset: usize = 0; | ||
| 27 | var timer = try Timer.start(); | 35 | var timer = try Timer.start(); |
| 28 | const start = timer.lap(); | 36 | const start = timer.lap(); |
| 29 | while (offset < BytesToHash) : (offset += block.len) { | 37 | while (offset < bytes) : (offset += block.len) { |
| 30 | h.update(block[0..]); | 38 | h.update(block[0..]); |
| 31 | } | 39 | } |
| 32 | const end = timer.read(); | 40 | const end = timer.read(); |
| 33 | 41 | ||
| 34 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; | 42 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; |
| 35 | const throughput = @floatToInt(u64, BytesToHash / elapsed_s); | 43 | const throughput = @floatToInt(u64, bytes / elapsed_s); |
| 44 | |||
| 45 | return throughput; | ||
| 46 | } | ||
| 47 | |||
| 48 | const macs = []Crypto{ | ||
| 49 | Crypto{ .ty = crypto.Poly1305, .name = "poly1305" }, | ||
| 50 | Crypto{ .ty = crypto.HmacMd5, .name = "hmac-md5" }, | ||
| 51 | Crypto{ .ty = crypto.HmacSha1, .name = "hmac-sha1" }, | ||
| 52 | Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" }, | ||
| 53 | }; | ||
| 54 | |||
| 55 | pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 { | ||
| 56 | std.debug.assert(32 >= Mac.mac_length and 32 >= Mac.minimum_key_length); | ||
| 57 | |||
| 58 | var in: [1 * MiB]u8 = undefined; | ||
| 59 | prng.random.bytes(in[0..]); | ||
| 60 | |||
| 61 | var key: [32]u8 = undefined; | ||
| 62 | prng.random.bytes(key[0..]); | ||
| 63 | |||
| 64 | var offset: usize = 0; | ||
| 65 | var timer = try Timer.start(); | ||
| 66 | const start = timer.lap(); | ||
| 67 | while (offset < bytes) : (offset += in.len) { | ||
| 68 | Mac.create(key[0..], in[0..], key); | ||
| 69 | } | ||
| 70 | const end = timer.read(); | ||
| 71 | |||
| 72 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; | ||
| 73 | const throughput = @floatToInt(u64, bytes / elapsed_s); | ||
| 74 | |||
| 75 | return throughput; | ||
| 76 | } | ||
| 77 | |||
| 78 | const exchanges = []Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }}; | ||
| 79 | |||
| 80 | pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 { | ||
| 81 | std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length); | ||
| 82 | |||
| 83 | var in: [DhKeyExchange.minimum_key_length]u8 = undefined; | ||
| 84 | prng.random.bytes(in[0..]); | ||
| 85 | |||
| 86 | var out: [DhKeyExchange.minimum_key_length]u8 = undefined; | ||
| 87 | prng.random.bytes(out[0..]); | ||
| 88 | |||
| 89 | var offset: usize = 0; | ||
| 90 | var timer = try Timer.start(); | ||
| 91 | const start = timer.lap(); | ||
| 92 | { | ||
| 93 | var i: usize = 0; | ||
| 94 | while (i < exchange_count) : (i += 1) { | ||
| 95 | _ = DhKeyExchange.create(out[0..], out, in); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | const end = timer.read(); | ||
| 99 | |||
| 100 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; | ||
| 101 | const throughput = @floatToInt(u64, exchange_count / elapsed_s); | ||
| 102 | |||
| 103 | return throughput; | ||
| 104 | } | ||
| 36 | 105 | ||
| 37 | try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); | 106 | fn usage() void { |
| 107 | std.debug.warn( | ||
| 108 | \\throughput_test [options] | ||
| 109 | \\ | ||
| 110 | \\Options: | ||
| 111 | \\ --filter [test-name] | ||
| 112 | \\ --seed [int] | ||
| 113 | \\ --help | ||
| 114 | \\ | ||
| 115 | ); | ||
| 116 | } | ||
| 117 | |||
| 118 | fn mode(comptime x: comptime_int) comptime_int { | ||
| 119 | return if (builtin.mode == builtin.Mode.Debug) x / 64 else x; | ||
| 120 | } | ||
| 121 | |||
| 122 | // TODO(#1358): Replace with builtin formatted padding when available. | ||
| 123 | fn printPad(stdout: var, s: []const u8) !void { | ||
| 124 | var i: usize = 0; | ||
| 125 | while (i < 12 - s.len) : (i += 1) { | ||
| 126 | try stdout.print(" "); | ||
| 127 | } | ||
| 128 | try stdout.print("{}", s); | ||
| 129 | } | ||
| 130 | |||
| 131 | pub fn main() !void { | ||
| 132 | var stdout_file = try std.io.getStdOut(); | ||
| 133 | var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); | ||
| 134 | const stdout = &stdout_out_stream.stream; | ||
| 135 | |||
| 136 | var buffer: [1024]u8 = undefined; | ||
| 137 | var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]); | ||
| 138 | const args = try std.os.argsAlloc(&fixed.allocator); | ||
| 139 | |||
| 140 | var filter: ?[]u8 = ""; | ||
| 141 | |||
| 142 | var i: usize = 1; | ||
| 143 | while (i < args.len) : (i += 1) { | ||
| 144 | if (std.mem.eql(u8, args[i], "--seed")) { | ||
| 145 | i += 1; | ||
| 146 | if (i == args.len) { | ||
| 147 | usage(); | ||
| 148 | std.os.exit(1); | ||
| 149 | } | ||
| 150 | |||
| 151 | const seed = try std.fmt.parseUnsigned(u32, args[i], 10); | ||
| 152 | prng.seed(seed); | ||
| 153 | } else if (std.mem.eql(u8, args[i], "--filter")) { | ||
| 154 | i += 1; | ||
| 155 | if (i == args.len) { | ||
| 156 | usage(); | ||
| 157 | std.os.exit(1); | ||
| 158 | } | ||
| 159 | |||
| 160 | filter = args[i]; | ||
| 161 | } else if (std.mem.eql(u8, args[i], "--help")) { | ||
| 162 | usage(); | ||
| 163 | return; | ||
| 164 | } else { | ||
| 165 | usage(); | ||
| 166 | std.os.exit(1); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | inline for (hashes) |H| { | ||
| 171 | if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { | ||
| 172 | const throughput = try benchmarkHash(H.ty, mode(32 * MiB)); | ||
| 173 | try printPad(stdout, H.name); | ||
| 174 | try stdout.print(": {} MiB/s\n", throughput / (1 * MiB)); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | inline for (macs) |M| { | ||
| 179 | if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) { | ||
| 180 | const throughput = try benchmarkMac(M.ty, mode(128 * MiB)); | ||
| 181 | try printPad(stdout, M.name); | ||
| 182 | try stdout.print(": {} MiB/s\n", throughput / (1 * MiB)); | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | inline for (exchanges) |E| { | ||
| 187 | if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) { | ||
| 188 | const throughput = try benchmarkKeyExchange(E.ty, mode(1000)); | ||
| 189 | try printPad(stdout, E.name); | ||
| 190 | try stdout.print(": {} exchanges/s\n", throughput); | ||
| 191 | } | ||
| 192 | } | ||
| 38 | } | 193 | } |
std/crypto/x25519.zig created+664| ... | @@ -0,0 +1,664 @@ | ||
| 1 | // Translated from monocypher which is licensed under CC-0/BSD-3. | ||
| 2 | // | ||
| 3 | // https://monocypher.org/ | ||
| 4 | |||
| 5 | const std = @import("../index.zig"); | ||
| 6 | const builtin = @import("builtin"); | ||
| 7 | |||
| 8 | const Endian = builtin.Endian; | ||
| 9 | const readInt = std.mem.readInt; | ||
| 10 | const writeInt = std.mem.writeInt; | ||
| 11 | |||
| 12 | // Based on Supercop's ref10 implementation. | ||
| 13 | pub const X25519 = struct { | ||
| 14 | pub const secret_length = 32; | ||
| 15 | pub const minimum_key_length = 32; | ||
| 16 | |||
| 17 | fn trimScalar(s: []u8) void { | ||
| 18 | s[0] &= 248; | ||
| 19 | s[31] &= 127; | ||
| 20 | s[31] |= 64; | ||
| 21 | } | ||
| 22 | |||
| 23 | fn scalarBit(s: []const u8, i: usize) i32 { | ||
| 24 | return (s[i >> 3] >> @intCast(u3, i & 7)) & 1; | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn create(out: []u8, private_key: []const u8, public_key: []const u8) bool { | ||
| 28 | std.debug.assert(out.len >= secret_length); | ||
| 29 | std.debug.assert(private_key.len >= minimum_key_length); | ||
| 30 | std.debug.assert(public_key.len >= minimum_key_length); | ||
| 31 | |||
| 32 | var storage: [7]Fe = undefined; | ||
| 33 | var x1 = &storage[0]; | ||
| 34 | var x2 = &storage[1]; | ||
| 35 | var z2 = &storage[2]; | ||
| 36 | var x3 = &storage[3]; | ||
| 37 | var z3 = &storage[4]; | ||
| 38 | var t0 = &storage[5]; | ||
| 39 | var t1 = &storage[6]; | ||
| 40 | |||
| 41 | // computes the scalar product | ||
| 42 | Fe.fromBytes(x1, public_key); | ||
| 43 | |||
| 44 | // restrict the possible scalar values | ||
| 45 | var e: [32]u8 = undefined; | ||
| 46 | for (e[0..]) |_, i| { | ||
| 47 | e[i] = private_key[i]; | ||
| 48 | } | ||
| 49 | trimScalar(e[0..]); | ||
| 50 | |||
| 51 | // computes the actual scalar product (the result is in x2 and z2) | ||
| 52 | |||
| 53 | // Montgomery ladder | ||
| 54 | // In projective coordinates, to avoid divisons: x = X / Z | ||
| 55 | // We don't care about the y coordinate, it's only 1 bit of information | ||
| 56 | Fe.init1(x2); | ||
| 57 | Fe.init0(z2); // "zero" point | ||
| 58 | Fe.copy(x3, x1); | ||
| 59 | Fe.init1(z3); | ||
| 60 | |||
| 61 | var swap: i32 = 0; | ||
| 62 | var pos: isize = 254; | ||
| 63 | while (pos >= 0) : (pos -= 1) { | ||
| 64 | // constant time conditional swap before ladder step | ||
| 65 | const b = scalarBit(e, @intCast(usize, pos)); | ||
| 66 | swap ^= b; // xor trick avoids swapping at the end of the loop | ||
| 67 | Fe.cswap(x2, x3, swap); | ||
| 68 | Fe.cswap(z2, z3, swap); | ||
| 69 | swap = b; // anticipates one last swap after the loop | ||
| 70 | |||
| 71 | // Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3) | ||
| 72 | // with differential addition | ||
| 73 | Fe.sub(t0, x3, z3); | ||
| 74 | Fe.sub(t1, x2, z2); | ||
| 75 | Fe.add(x2, x2, z2); | ||
| 76 | Fe.add(z2, x3, z3); | ||
| 77 | Fe.mul(z3, t0, x2); | ||
| 78 | Fe.mul(z2, z2, t1); | ||
| 79 | Fe.sq(t0, t1); | ||
| 80 | Fe.sq(t1, x2); | ||
| 81 | Fe.add(x3, z3, z2); | ||
| 82 | Fe.sub(z2, z3, z2); | ||
| 83 | Fe.mul(x2, t1, t0); | ||
| 84 | Fe.sub(t1, t1, t0); | ||
| 85 | Fe.sq(z2, z2); | ||
| 86 | Fe.mulSmall(z3, t1, 121666); | ||
| 87 | Fe.sq(x3, x3); | ||
| 88 | Fe.add(t0, t0, z3); | ||
| 89 | Fe.mul(z3, x1, z2); | ||
| 90 | Fe.mul(z2, t1, t0); | ||
| 91 | } | ||
| 92 | |||
| 93 | // last swap is necessary to compensate for the xor trick | ||
| 94 | // Note: after this swap, P3 == P2 + P1. | ||
| 95 | Fe.cswap(x2, x3, swap); | ||
| 96 | Fe.cswap(z2, z3, swap); | ||
| 97 | |||
| 98 | // normalises the coordinates: x == X / Z | ||
| 99 | Fe.invert(z2, z2); | ||
| 100 | Fe.mul(x2, x2, z2); | ||
| 101 | Fe.toBytes(out, x2); | ||
| 102 | |||
| 103 | x1.secureZero(); | ||
| 104 | x2.secureZero(); | ||
| 105 | x3.secureZero(); | ||
| 106 | t0.secureZero(); | ||
| 107 | t1.secureZero(); | ||
| 108 | z2.secureZero(); | ||
| 109 | z3.secureZero(); | ||
| 110 | std.mem.secureZero(u8, e[0..]); | ||
| 111 | |||
| 112 | // Returns false if the output is all zero | ||
| 113 | // (happens with some malicious public keys) | ||
| 114 | return !zerocmp(u8, out); | ||
| 115 | } | ||
| 116 | |||
| 117 | pub fn createPublicKey(public_key: []const u8, private_key: []const u8) bool { | ||
| 118 | var base_point = []u8{9} ++ []u8{0} ** 31; | ||
| 119 | return create(public_key, private_key, base_point); | ||
| 120 | } | ||
| 121 | }; | ||
| 122 | |||
| 123 | // Constant time compare to zero. | ||
| 124 | fn zerocmp(comptime T: type, a: []const T) bool { | ||
| 125 | var s: T = 0; | ||
| 126 | for (a) |b| { | ||
| 127 | s |= b; | ||
| 128 | } | ||
| 129 | return s == 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | //////////////////////////////////// | ||
| 133 | /// Arithmetic modulo 2^255 - 19 /// | ||
| 134 | //////////////////////////////////// | ||
| 135 | // Taken from Supercop's ref10 implementation. | ||
| 136 | // A bit bigger than TweetNaCl, over 4 times faster. | ||
| 137 | |||
| 138 | // field element | ||
| 139 | const Fe = struct { | ||
| 140 | b: [10]i32, | ||
| 141 | |||
| 142 | fn secureZero(self: *Fe) void { | ||
| 143 | std.mem.secureZero(u8, @ptrCast([*]u8, self)[0..@sizeOf(Fe)]); | ||
| 144 | } | ||
| 145 | |||
| 146 | fn init0(h: *Fe) void { | ||
| 147 | for (h.b) |*e| { | ||
| 148 | e.* = 0; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | fn init1(h: *Fe) void { | ||
| 153 | for (h.b[1..]) |*e| { | ||
| 154 | e.* = 0; | ||
| 155 | } | ||
| 156 | h.b[0] = 1; | ||
| 157 | } | ||
| 158 | |||
| 159 | fn copy(h: *Fe, f: *const Fe) void { | ||
| 160 | for (h.b) |_, i| { | ||
| 161 | h.b[i] = f.b[i]; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | fn neg(h: *Fe, f: *const Fe) void { | ||
| 166 | for (h.b) |_, i| { | ||
| 167 | h.b[i] = -f.b[i]; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | fn add(h: *Fe, f: *const Fe, g: *const Fe) void { | ||
| 172 | for (h.b) |_, i| { | ||
| 173 | h.b[i] = f.b[i] + g.b[i]; | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | fn sub(h: *Fe, f: *const Fe, g: *const Fe) void { | ||
| 178 | for (h.b) |_, i| { | ||
| 179 | h.b[i] = f.b[i] - g.b[i]; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | fn cswap(f: *Fe, g: *Fe, b: i32) void { | ||
| 184 | for (f.b) |_, i| { | ||
| 185 | const x = (f.b[i] ^ g.b[i]) & -b; | ||
| 186 | f.b[i] ^= x; | ||
| 187 | g.b[i] ^= x; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | fn ccopy(f: *Fe, g: *const Fe, b: i32) void { | ||
| 192 | for (f.b) |_, i| { | ||
| 193 | const x = (f.b[i] ^ g.b[i]) & -b; | ||
| 194 | f.b[i] ^= x; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | inline fn carryRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int, comptime mult: comptime_int) void { | ||
| 199 | const j = (i + 1) % 10; | ||
| 200 | |||
| 201 | c[i] = (t[i] + (i64(1) << shift)) >> (shift + 1); | ||
| 202 | t[j] += c[i] * mult; | ||
| 203 | t[i] -= c[i] * (i64(1) << (shift + 1)); | ||
| 204 | } | ||
| 205 | |||
| 206 | fn carry1(h: *Fe, t: []i64) void { | ||
| 207 | var c: [10]i64 = undefined; | ||
| 208 | |||
| 209 | var sc = c[0..]; | ||
| 210 | var st = t[0..]; | ||
| 211 | |||
| 212 | carryRound(sc, st, 9, 24, 19); | ||
| 213 | carryRound(sc, st, 1, 24, 1); | ||
| 214 | carryRound(sc, st, 3, 24, 1); | ||
| 215 | carryRound(sc, st, 5, 24, 1); | ||
| 216 | carryRound(sc, st, 7, 24, 1); | ||
| 217 | carryRound(sc, st, 0, 25, 1); | ||
| 218 | carryRound(sc, st, 2, 25, 1); | ||
| 219 | carryRound(sc, st, 4, 25, 1); | ||
| 220 | carryRound(sc, st, 6, 25, 1); | ||
| 221 | carryRound(sc, st, 8, 25, 1); | ||
| 222 | |||
| 223 | for (h.b) |_, i| { | ||
| 224 | h.b[i] = @intCast(i32, t[i]); | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | fn carry2(h: *Fe, t: []i64) void { | ||
| 229 | var c: [10]i64 = undefined; | ||
| 230 | |||
| 231 | var sc = c[0..]; | ||
| 232 | var st = t[0..]; | ||
| 233 | |||
| 234 | carryRound(sc, st, 0, 25, 1); | ||
| 235 | carryRound(sc, st, 4, 25, 1); | ||
| 236 | carryRound(sc, st, 1, 24, 1); | ||
| 237 | carryRound(sc, st, 5, 24, 1); | ||
| 238 | carryRound(sc, st, 2, 25, 1); | ||
| 239 | carryRound(sc, st, 6, 25, 1); | ||
| 240 | carryRound(sc, st, 3, 24, 1); | ||
| 241 | carryRound(sc, st, 7, 24, 1); | ||
| 242 | carryRound(sc, st, 4, 25, 1); | ||
| 243 | carryRound(sc, st, 8, 25, 1); | ||
| 244 | carryRound(sc, st, 9, 24, 19); | ||
| 245 | carryRound(sc, st, 0, 25, 1); | ||
| 246 | |||
| 247 | for (h.b) |_, i| { | ||
| 248 | h.b[i] = @intCast(i32, t[i]); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | fn fromBytes(h: *Fe, s: []const u8) void { | ||
| 253 | std.debug.assert(s.len >= 32); | ||
| 254 | |||
| 255 | var t: [10]i64 = undefined; | ||
| 256 | |||
| 257 | t[0] = readInt(s[0..4], u32, Endian.Little); | ||
| 258 | t[1] = readInt(s[4..7], u32, Endian.Little) << 6; | ||
| 259 | t[2] = readInt(s[7..10], u32, Endian.Little) << 5; | ||
| 260 | t[3] = readInt(s[10..13], u32, Endian.Little) << 3; | ||
| 261 | t[4] = readInt(s[13..16], u32, Endian.Little) << 2; | ||
| 262 | t[5] = readInt(s[16..20], u32, Endian.Little); | ||
| 263 | t[6] = readInt(s[20..23], u32, Endian.Little) << 7; | ||
| 264 | t[7] = readInt(s[23..26], u32, Endian.Little) << 5; | ||
| 265 | t[8] = readInt(s[26..29], u32, Endian.Little) << 4; | ||
| 266 | t[9] = (readInt(s[29..32], u32, Endian.Little) & 0x7fffff) << 2; | ||
| 267 | |||
| 268 | carry1(h, t[0..]); | ||
| 269 | } | ||
| 270 | |||
| 271 | fn mulSmall(h: *Fe, f: *const Fe, comptime g: comptime_int) void { | ||
| 272 | var t: [10]i64 = undefined; | ||
| 273 | |||
| 274 | for (t[0..]) |_, i| { | ||
| 275 | t[i] = i64(f.b[i]) * g; | ||
| 276 | } | ||
| 277 | |||
| 278 | carry1(h, t[0..]); | ||
| 279 | } | ||
| 280 | |||
| 281 | fn mul(h: *Fe, f1: *const Fe, g1: *const Fe) void { | ||
| 282 | const f = f1.b; | ||
| 283 | const g = g1.b; | ||
| 284 | |||
| 285 | var F: [10]i32 = undefined; | ||
| 286 | var G: [10]i32 = undefined; | ||
| 287 | |||
| 288 | F[1] = f[1] * 2; | ||
| 289 | F[3] = f[3] * 2; | ||
| 290 | F[5] = f[5] * 2; | ||
| 291 | F[7] = f[7] * 2; | ||
| 292 | F[9] = f[9] * 2; | ||
| 293 | |||
| 294 | G[1] = g[1] * 19; | ||
| 295 | G[2] = g[2] * 19; | ||
| 296 | G[3] = g[3] * 19; | ||
| 297 | G[4] = g[4] * 19; | ||
| 298 | G[5] = g[5] * 19; | ||
| 299 | G[6] = g[6] * 19; | ||
| 300 | G[7] = g[7] * 19; | ||
| 301 | G[8] = g[8] * 19; | ||
| 302 | G[9] = g[9] * 19; | ||
| 303 | |||
| 304 | // t's become h | ||
| 305 | var t: [10]i64 = undefined; | ||
| 306 | |||
| 307 | t[0] = f[0] * i64(g[0]) + F[1] * i64(G[9]) + f[2] * i64(G[8]) + F[3] * i64(G[7]) + f[4] * i64(G[6]) + F[5] * i64(G[5]) + f[6] * i64(G[4]) + F[7] * i64(G[3]) + f[8] * i64(G[2]) + F[9] * i64(G[1]); | ||
| 308 | t[1] = f[0] * i64(g[1]) + f[1] * i64(g[0]) + f[2] * i64(G[9]) + f[3] * i64(G[8]) + f[4] * i64(G[7]) + f[5] * i64(G[6]) + f[6] * i64(G[5]) + f[7] * i64(G[4]) + f[8] * i64(G[3]) + f[9] * i64(G[2]); | ||
| 309 | t[2] = f[0] * i64(g[2]) + F[1] * i64(g[1]) + f[2] * i64(g[0]) + F[3] * i64(G[9]) + f[4] * i64(G[8]) + F[5] * i64(G[7]) + f[6] * i64(G[6]) + F[7] * i64(G[5]) + f[8] * i64(G[4]) + F[9] * i64(G[3]); | ||
| 310 | t[3] = f[0] * i64(g[3]) + f[1] * i64(g[2]) + f[2] * i64(g[1]) + f[3] * i64(g[0]) + f[4] * i64(G[9]) + f[5] * i64(G[8]) + f[6] * i64(G[7]) + f[7] * i64(G[6]) + f[8] * i64(G[5]) + f[9] * i64(G[4]); | ||
| 311 | t[4] = f[0] * i64(g[4]) + F[1] * i64(g[3]) + f[2] * i64(g[2]) + F[3] * i64(g[1]) + f[4] * i64(g[0]) + F[5] * i64(G[9]) + f[6] * i64(G[8]) + F[7] * i64(G[7]) + f[8] * i64(G[6]) + F[9] * i64(G[5]); | ||
| 312 | t[5] = f[0] * i64(g[5]) + f[1] * i64(g[4]) + f[2] * i64(g[3]) + f[3] * i64(g[2]) + f[4] * i64(g[1]) + f[5] * i64(g[0]) + f[6] * i64(G[9]) + f[7] * i64(G[8]) + f[8] * i64(G[7]) + f[9] * i64(G[6]); | ||
| 313 | t[6] = f[0] * i64(g[6]) + F[1] * i64(g[5]) + f[2] * i64(g[4]) + F[3] * i64(g[3]) + f[4] * i64(g[2]) + F[5] * i64(g[1]) + f[6] * i64(g[0]) + F[7] * i64(G[9]) + f[8] * i64(G[8]) + F[9] * i64(G[7]); | ||
| 314 | t[7] = f[0] * i64(g[7]) + f[1] * i64(g[6]) + f[2] * i64(g[5]) + f[3] * i64(g[4]) + f[4] * i64(g[3]) + f[5] * i64(g[2]) + f[6] * i64(g[1]) + f[7] * i64(g[0]) + f[8] * i64(G[9]) + f[9] * i64(G[8]); | ||
| 315 | t[8] = f[0] * i64(g[8]) + F[1] * i64(g[7]) + f[2] * i64(g[6]) + F[3] * i64(g[5]) + f[4] * i64(g[4]) + F[5] * i64(g[3]) + f[6] * i64(g[2]) + F[7] * i64(g[1]) + f[8] * i64(g[0]) + F[9] * i64(G[9]); | ||
| 316 | t[9] = f[0] * i64(g[9]) + f[1] * i64(g[8]) + f[2] * i64(g[7]) + f[3] * i64(g[6]) + f[4] * i64(g[5]) + f[5] * i64(g[4]) + f[6] * i64(g[3]) + f[7] * i64(g[2]) + f[8] * i64(g[1]) + f[9] * i64(g[0]); | ||
| 317 | |||
| 318 | carry2(h, t[0..]); | ||
| 319 | } | ||
| 320 | |||
| 321 | // we could use Fe.mul() for this, but this is significantly faster | ||
| 322 | fn sq(h: *Fe, fz: *const Fe) void { | ||
| 323 | const f0 = fz.b[0]; | ||
| 324 | const f1 = fz.b[1]; | ||
| 325 | const f2 = fz.b[2]; | ||
| 326 | const f3 = fz.b[3]; | ||
| 327 | const f4 = fz.b[4]; | ||
| 328 | const f5 = fz.b[5]; | ||
| 329 | const f6 = fz.b[6]; | ||
| 330 | const f7 = fz.b[7]; | ||
| 331 | const f8 = fz.b[8]; | ||
| 332 | const f9 = fz.b[9]; | ||
| 333 | |||
| 334 | const f0_2 = f0 * 2; | ||
| 335 | const f1_2 = f1 * 2; | ||
| 336 | const f2_2 = f2 * 2; | ||
| 337 | const f3_2 = f3 * 2; | ||
| 338 | const f4_2 = f4 * 2; | ||
| 339 | const f5_2 = f5 * 2; | ||
| 340 | const f6_2 = f6 * 2; | ||
| 341 | const f7_2 = f7 * 2; | ||
| 342 | const f5_38 = f5 * 38; | ||
| 343 | const f6_19 = f6 * 19; | ||
| 344 | const f7_38 = f7 * 38; | ||
| 345 | const f8_19 = f8 * 19; | ||
| 346 | const f9_38 = f9 * 38; | ||
| 347 | |||
| 348 | var t: [10]i64 = undefined; | ||
| 349 | |||
| 350 | t[0] = f0 * i64(f0) + f1_2 * i64(f9_38) + f2_2 * i64(f8_19) + f3_2 * i64(f7_38) + f4_2 * i64(f6_19) + f5 * i64(f5_38); | ||
| 351 | t[1] = f0_2 * i64(f1) + f2 * i64(f9_38) + f3_2 * i64(f8_19) + f4 * i64(f7_38) + f5_2 * i64(f6_19); | ||
| 352 | t[2] = f0_2 * i64(f2) + f1_2 * i64(f1) + f3_2 * i64(f9_38) + f4_2 * i64(f8_19) + f5_2 * i64(f7_38) + f6 * i64(f6_19); | ||
| 353 | t[3] = f0_2 * i64(f3) + f1_2 * i64(f2) + f4 * i64(f9_38) + f5_2 * i64(f8_19) + f6 * i64(f7_38); | ||
| 354 | t[4] = f0_2 * i64(f4) + f1_2 * i64(f3_2) + f2 * i64(f2) + f5_2 * i64(f9_38) + f6_2 * i64(f8_19) + f7 * i64(f7_38); | ||
| 355 | t[5] = f0_2 * i64(f5) + f1_2 * i64(f4) + f2_2 * i64(f3) + f6 * i64(f9_38) + f7_2 * i64(f8_19); | ||
| 356 | t[6] = f0_2 * i64(f6) + f1_2 * i64(f5_2) + f2_2 * i64(f4) + f3_2 * i64(f3) + f7_2 * i64(f9_38) + f8 * i64(f8_19); | ||
| 357 | t[7] = f0_2 * i64(f7) + f1_2 * i64(f6) + f2_2 * i64(f5) + f3_2 * i64(f4) + f8 * i64(f9_38); | ||
| 358 | t[8] = f0_2 * i64(f8) + f1_2 * i64(f7_2) + f2_2 * i64(f6) + f3_2 * i64(f5_2) + f4 * i64(f4) + f9 * i64(f9_38); | ||
| 359 | t[9] = f0_2 * i64(f9) + f1_2 * i64(f8) + f2_2 * i64(f7) + f3_2 * i64(f6) + f4 * i64(f5_2); | ||
| 360 | |||
| 361 | carry2(h, t[0..]); | ||
| 362 | } | ||
| 363 | |||
| 364 | fn sq2(h: *Fe, f: *const Fe) void { | ||
| 365 | Fe.sq(h, f); | ||
| 366 | Fe.mul_small(h, h, 2); | ||
| 367 | } | ||
| 368 | |||
| 369 | // This could be simplified, but it would be slower | ||
| 370 | fn invert(out: *Fe, z: *const Fe) void { | ||
| 371 | var i: usize = undefined; | ||
| 372 | |||
| 373 | var t: [4]Fe = undefined; | ||
| 374 | var t0 = &t[0]; | ||
| 375 | var t1 = &t[1]; | ||
| 376 | var t2 = &t[2]; | ||
| 377 | var t3 = &t[3]; | ||
| 378 | |||
| 379 | Fe.sq(t0, z); | ||
| 380 | Fe.sq(t1, t0); | ||
| 381 | Fe.sq(t1, t1); | ||
| 382 | Fe.mul(t1, z, t1); | ||
| 383 | Fe.mul(t0, t0, t1); | ||
| 384 | |||
| 385 | Fe.sq(t2, t0); | ||
| 386 | Fe.mul(t1, t1, t2); | ||
| 387 | |||
| 388 | Fe.sq(t2, t1); | ||
| 389 | i = 1; | ||
| 390 | while (i < 5) : (i += 1) Fe.sq(t2, t2); | ||
| 391 | Fe.mul(t1, t2, t1); | ||
| 392 | |||
| 393 | Fe.sq(t2, t1); | ||
| 394 | i = 1; | ||
| 395 | while (i < 10) : (i += 1) Fe.sq(t2, t2); | ||
| 396 | Fe.mul(t2, t2, t1); | ||
| 397 | |||
| 398 | Fe.sq(t3, t2); | ||
| 399 | i = 1; | ||
| 400 | while (i < 20) : (i += 1) Fe.sq(t3, t3); | ||
| 401 | Fe.mul(t2, t3, t2); | ||
| 402 | |||
| 403 | Fe.sq(t2, t2); | ||
| 404 | i = 1; | ||
| 405 | while (i < 10) : (i += 1) Fe.sq(t2, t2); | ||
| 406 | Fe.mul(t1, t2, t1); | ||
| 407 | |||
| 408 | Fe.sq(t2, t1); | ||
| 409 | i = 1; | ||
| 410 | while (i < 50) : (i += 1) Fe.sq(t2, t2); | ||
| 411 | Fe.mul(t2, t2, t1); | ||
| 412 | |||
| 413 | Fe.sq(t3, t2); | ||
| 414 | i = 1; | ||
| 415 | while (i < 100) : (i += 1) Fe.sq(t3, t3); | ||
| 416 | Fe.mul(t2, t3, t2); | ||
| 417 | |||
| 418 | Fe.sq(t2, t2); | ||
| 419 | i = 1; | ||
| 420 | while (i < 50) : (i += 1) Fe.sq(t2, t2); | ||
| 421 | Fe.mul(t1, t2, t1); | ||
| 422 | |||
| 423 | Fe.sq(t1, t1); | ||
| 424 | i = 1; | ||
| 425 | while (i < 5) : (i += 1) Fe.sq(t1, t1); | ||
| 426 | Fe.mul(out, t1, t0); | ||
| 427 | |||
| 428 | t0.secureZero(); | ||
| 429 | t1.secureZero(); | ||
| 430 | t2.secureZero(); | ||
| 431 | t3.secureZero(); | ||
| 432 | } | ||
| 433 | |||
| 434 | // This could be simplified, but it would be slower | ||
| 435 | fn pow22523(out: *Fe, z: *const Fe) void { | ||
| 436 | var i: usize = undefined; | ||
| 437 | |||
| 438 | var t: [3]Fe = undefined; | ||
| 439 | var t0 = &t[0]; | ||
| 440 | var t1 = &t[1]; | ||
| 441 | var t2 = &t[2]; | ||
| 442 | |||
| 443 | Fe.sq(t0, z); | ||
| 444 | Fe.sq(t1, t0); | ||
| 445 | Fe.sq(t1, t1); | ||
| 446 | Fe.mul(t1, z, t1); | ||
| 447 | Fe.mul(t0, t0, t1); | ||
| 448 | |||
| 449 | Fe.sq(t0, t0); | ||
| 450 | Fe.mul(t0, t1, t0); | ||
| 451 | |||
| 452 | Fe.sq(t1, t0); | ||
| 453 | i = 1; | ||
| 454 | while (i < 5) : (i += 1) Fe.sq(t1, t1); | ||
| 455 | Fe.mul(t0, t1, t0); | ||
| 456 | |||
| 457 | Fe.sq(t1, t0); | ||
| 458 | i = 1; | ||
| 459 | while (i < 10) : (i += 1) Fe.sq(t1, t1); | ||
| 460 | Fe.mul(t1, t1, t0); | ||
| 461 | |||
| 462 | Fe.sq(t2, t1); | ||
| 463 | i = 1; | ||
| 464 | while (i < 20) : (i += 1) Fe.sq(t2, t2); | ||
| 465 | Fe.mul(t1, t2, t1); | ||
| 466 | |||
| 467 | Fe.sq(t1, t1); | ||
| 468 | i = 1; | ||
| 469 | while (i < 10) : (i += 1) Fe.sq(t1, t1); | ||
| 470 | Fe.mul(t0, t1, t0); | ||
| 471 | |||
| 472 | Fe.sq(t1, t0); | ||
| 473 | i = 1; | ||
| 474 | while (i < 50) : (i += 1) Fe.sq(t1, t1); | ||
| 475 | Fe.mul(t1, t1, t0); | ||
| 476 | |||
| 477 | Fe.sq(t2, t1); | ||
| 478 | i = 1; | ||
| 479 | while (i < 100) : (i += 1) Fe.sq(t2, t2); | ||
| 480 | Fe.mul(t1, t2, t1); | ||
| 481 | |||
| 482 | Fe.sq(t1, t1); | ||
| 483 | i = 1; | ||
| 484 | while (i < 50) : (i += 1) Fe.sq(t1, t1); | ||
| 485 | Fe.mul(t0, t1, t0); | ||
| 486 | |||
| 487 | Fe.sq(t0, t0); | ||
| 488 | i = 1; | ||
| 489 | while (i < 2) : (i += 1) Fe.sq(t0, t0); | ||
| 490 | Fe.mul(out, t0, z); | ||
| 491 | |||
| 492 | t0.secureZero(); | ||
| 493 | t1.secureZero(); | ||
| 494 | t2.secureZero(); | ||
| 495 | } | ||
| 496 | |||
| 497 | inline fn toBytesRound(c: []i64, t: []i64, comptime i: comptime_int, comptime shift: comptime_int) void { | ||
| 498 | c[i] = t[i] >> shift; | ||
| 499 | if (i + 1 < 10) { | ||
| 500 | t[i + 1] += c[i]; | ||
| 501 | } | ||
| 502 | t[i] -= c[i] * (i32(1) << shift); | ||
| 503 | } | ||
| 504 | |||
| 505 | fn toBytes(s: []u8, h: *const Fe) void { | ||
| 506 | std.debug.assert(s.len >= 32); | ||
| 507 | |||
| 508 | var t: [10]i64 = undefined; | ||
| 509 | for (h.b[0..]) |_, i| { | ||
| 510 | t[i] = h.b[i]; | ||
| 511 | } | ||
| 512 | |||
| 513 | var q = (19 * t[9] + ((i32(1) << 24))) >> 25; | ||
| 514 | { | ||
| 515 | var i: usize = 0; | ||
| 516 | while (i < 5) : (i += 1) { | ||
| 517 | q += t[2 * i]; | ||
| 518 | q >>= 26; | ||
| 519 | q += t[2 * i + 1]; | ||
| 520 | q >>= 25; | ||
| 521 | } | ||
| 522 | } | ||
| 523 | t[0] += 19 * q; | ||
| 524 | |||
| 525 | var c: [10]i64 = undefined; | ||
| 526 | |||
| 527 | var st = t[0..]; | ||
| 528 | var sc = c[0..]; | ||
| 529 | |||
| 530 | toBytesRound(sc, st, 0, 26); | ||
| 531 | toBytesRound(sc, st, 1, 25); | ||
| 532 | toBytesRound(sc, st, 2, 26); | ||
| 533 | toBytesRound(sc, st, 3, 25); | ||
| 534 | toBytesRound(sc, st, 4, 26); | ||
| 535 | toBytesRound(sc, st, 5, 25); | ||
| 536 | toBytesRound(sc, st, 6, 26); | ||
| 537 | toBytesRound(sc, st, 7, 25); | ||
| 538 | toBytesRound(sc, st, 8, 26); | ||
| 539 | toBytesRound(sc, st, 9, 25); | ||
| 540 | |||
| 541 | var ut: [10]u32 = undefined; | ||
| 542 | for (ut[0..]) |_, i| { | ||
| 543 | ut[i] = @bitCast(u32, @intCast(i32, t[i])); | ||
| 544 | } | ||
| 545 | |||
| 546 | writeInt(s[0..], (ut[0] >> 0) | (ut[1] << 26), Endian.Little); | ||
| 547 | writeInt(s[4..], (ut[1] >> 6) | (ut[2] << 19), Endian.Little); | ||
| 548 | writeInt(s[8..], (ut[2] >> 13) | (ut[3] << 13), Endian.Little); | ||
| 549 | writeInt(s[12..], (ut[3] >> 19) | (ut[4] << 6), Endian.Little); | ||
| 550 | writeInt(s[16..], (ut[5] >> 0) | (ut[6] << 25), Endian.Little); | ||
| 551 | writeInt(s[20..], (ut[6] >> 7) | (ut[7] << 19), Endian.Little); | ||
| 552 | writeInt(s[24..], (ut[7] >> 13) | (ut[8] << 12), Endian.Little); | ||
| 553 | writeInt(s[28..], (ut[8] >> 20) | (ut[9] << 6), Endian.Little); | ||
| 554 | |||
| 555 | std.mem.secureZero(i64, t[0..]); | ||
| 556 | } | ||
| 557 | |||
| 558 | // Parity check. Returns 0 if even, 1 if odd | ||
| 559 | fn isNegative(f: *const Fe) bool { | ||
| 560 | var s: [32]u8 = undefined; | ||
| 561 | Fe.toBytes(s[0..], f); | ||
| 562 | const isneg = s[0] & 1; | ||
| 563 | s.secureZero(); | ||
| 564 | return isneg; | ||
| 565 | } | ||
| 566 | |||
| 567 | fn isNonZero(f: *const Fe) bool { | ||
| 568 | var s: [32]u8 = undefined; | ||
| 569 | Fe.toBytes(s[0..], f); | ||
| 570 | const isnonzero = zerocmp(u8, s[0..]); | ||
| 571 | s.secureZero(); | ||
| 572 | return isneg; | ||
| 573 | } | ||
| 574 | }; | ||
| 575 | |||
| 576 | test "x25519 rfc7748 vector1" { | ||
| 577 | const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4"; | ||
| 578 | const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c"; | ||
| 579 | |||
| 580 | const expected_output = "\xc3\xda\x55\x37\x9d\xe9\xc6\x90\x8e\x94\xea\x4d\xf2\x8d\x08\x4f\x32\xec\xcf\x03\x49\x1c\x71\xf7\x54\xb4\x07\x55\x77\xa2\x85\x52"; | ||
| 581 | |||
| 582 | var output: [32]u8 = undefined; | ||
| 583 | |||
| 584 | std.debug.assert(X25519.create(output[0..], secret_key, public_key)); | ||
| 585 | std.debug.assert(std.mem.eql(u8, output, expected_output)); | ||
| 586 | } | ||
| 587 | |||
| 588 | test "x25519 rfc7748 vector2" { | ||
| 589 | const secret_key = "\x4b\x66\xe9\xd4\xd1\xb4\x67\x3c\x5a\xd2\x26\x91\x95\x7d\x6a\xf5\xc1\x1b\x64\x21\xe0\xea\x01\xd4\x2c\xa4\x16\x9e\x79\x18\xba\x0d"; | ||
| 590 | const public_key = "\xe5\x21\x0f\x12\x78\x68\x11\xd3\xf4\xb7\x95\x9d\x05\x38\xae\x2c\x31\xdb\xe7\x10\x6f\xc0\x3c\x3e\xfc\x4c\xd5\x49\xc7\x15\xa4\x93"; | ||
| 591 | |||
| 592 | const expected_output = "\x95\xcb\xde\x94\x76\xe8\x90\x7d\x7a\xad\xe4\x5c\xb4\xb8\x73\xf8\x8b\x59\x5a\x68\x79\x9f\xa1\x52\xe6\xf8\xf7\x64\x7a\xac\x79\x57"; | ||
| 593 | |||
| 594 | var output: [32]u8 = undefined; | ||
| 595 | |||
| 596 | std.debug.assert(X25519.create(output[0..], secret_key, public_key)); | ||
| 597 | std.debug.assert(std.mem.eql(u8, output, expected_output)); | ||
| 598 | } | ||
| 599 | |||
| 600 | test "x25519 rfc7748 one iteration" { | ||
| 601 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; | ||
| 602 | const expected_output = "\x42\x2c\x8e\x7a\x62\x27\xd7\xbc\xa1\x35\x0b\x3e\x2b\xb7\x27\x9f\x78\x97\xb8\x7b\xb6\x85\x4b\x78\x3c\x60\xe8\x03\x11\xae\x30\x79"; | ||
| 603 | |||
| 604 | var k: [32]u8 = initial_value; | ||
| 605 | var u: [32]u8 = initial_value; | ||
| 606 | |||
| 607 | var i: usize = 0; | ||
| 608 | while (i < 1) : (i += 1) { | ||
| 609 | var output: [32]u8 = undefined; | ||
| 610 | std.debug.assert(X25519.create(output[0..], k, u)); | ||
| 611 | |||
| 612 | std.mem.copy(u8, u[0..], k[0..]); | ||
| 613 | std.mem.copy(u8, k[0..], output[0..]); | ||
| 614 | } | ||
| 615 | |||
| 616 | std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); | ||
| 617 | } | ||
| 618 | |||
| 619 | test "x25519 rfc7748 1,000 iterations" { | ||
| 620 | // These iteration tests are slow so we always skip them. Results have been verified. | ||
| 621 | if (true) { | ||
| 622 | return error.SkipZigTest; | ||
| 623 | } | ||
| 624 | |||
| 625 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; | ||
| 626 | const expected_output = "\x68\x4c\xf5\x9b\xa8\x33\x09\x55\x28\x00\xef\x56\x6f\x2f\x4d\x3c\x1c\x38\x87\xc4\x93\x60\xe3\x87\x5f\x2e\xb9\x4d\x99\x53\x2c\x51"; | ||
| 627 | |||
| 628 | var k: [32]u8 = initial_value; | ||
| 629 | var u: [32]u8 = initial_value; | ||
| 630 | |||
| 631 | var i: usize = 0; | ||
| 632 | while (i < 1000) : (i += 1) { | ||
| 633 | var output: [32]u8 = undefined; | ||
| 634 | std.debug.assert(X25519.create(output[0..], k, u)); | ||
| 635 | |||
| 636 | std.mem.copy(u8, u[0..], k[0..]); | ||
| 637 | std.mem.copy(u8, k[0..], output[0..]); | ||
| 638 | } | ||
| 639 | |||
| 640 | std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); | ||
| 641 | } | ||
| 642 | |||
| 643 | test "x25519 rfc7748 1,000,000 iterations" { | ||
| 644 | if (true) { | ||
| 645 | return error.SkipZigTest; | ||
| 646 | } | ||
| 647 | |||
| 648 | const initial_value = "\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; | ||
| 649 | const expected_output = "\x7c\x39\x11\xe0\xab\x25\x86\xfd\x86\x44\x97\x29\x7e\x57\x5e\x6f\x3b\xc6\x01\xc0\x88\x3c\x30\xdf\x5f\x4d\xd2\xd2\x4f\x66\x54\x24"; | ||
| 650 | |||
| 651 | var k: [32]u8 = initial_value; | ||
| 652 | var u: [32]u8 = initial_value; | ||
| 653 | |||
| 654 | var i: usize = 0; | ||
| 655 | while (i < 1000000) : (i += 1) { | ||
| 656 | var output: [32]u8 = undefined; | ||
| 657 | std.debug.assert(X25519.create(output[0..], k, u)); | ||
| 658 | |||
| 659 | std.mem.copy(u8, u[0..], k[0..]); | ||
| 660 | std.mem.copy(u8, k[0..], output[0..]); | ||
| 661 | } | ||
| 662 | |||
| 663 | std.debug.assert(std.mem.eql(u8, k[0..], expected_output)); | ||
| 664 | } | ||
std/debug/index.zig+494-20| ... | @@ -4,8 +4,11 @@ const mem = std.mem; | ... | @@ -4,8 +4,11 @@ const mem = std.mem; |
| 4 | const io = std.io; | 4 | const io = std.io; |
| 5 | const os = std.os; | 5 | const os = std.os; |
| 6 | const elf = std.elf; | 6 | const elf = std.elf; |
| 7 | const macho = std.macho; | ||
| 8 | const DW = std.dwarf; | 7 | const DW = std.dwarf; |
| 8 | const macho = std.macho; | ||
| 9 | const coff = std.coff; | ||
| 10 | const pdb = std.pdb; | ||
| 11 | const windows = os.windows; | ||
| 9 | const ArrayList = std.ArrayList; | 12 | const ArrayList = std.ArrayList; |
| 10 | const builtin = @import("builtin"); | 13 | const builtin = @import("builtin"); |
| 11 | 14 | ||
| ... | @@ -17,6 +20,17 @@ pub const runtime_safety = switch (builtin.mode) { | ... | @@ -17,6 +20,17 @@ pub const runtime_safety = switch (builtin.mode) { |
| 17 | builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, | 20 | builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, |
| 18 | }; | 21 | }; |
| 19 | 22 | ||
| 23 | const Module = struct { | ||
| 24 | mod_info: pdb.ModInfo, | ||
| 25 | module_name: []u8, | ||
| 26 | obj_file_name: []u8, | ||
| 27 | |||
| 28 | populated: bool, | ||
| 29 | symbols: []u8, | ||
| 30 | subsect_info: []u8, | ||
| 31 | checksum_offset: ?usize, | ||
| 32 | }; | ||
| 33 | |||
| 20 | /// Tries to write to stderr, unbuffered, and ignores any error returned. | 34 | /// Tries to write to stderr, unbuffered, and ignores any error returned. |
| 21 | /// Does not append a newline. | 35 | /// Does not append a newline. |
| 22 | var stderr_file: os.File = undefined; | 36 | var stderr_file: os.File = undefined; |
| ... | @@ -37,7 +51,7 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { | ... | @@ -37,7 +51,7 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { |
| 37 | return st; | 51 | return st; |
| 38 | } else { | 52 | } else { |
| 39 | stderr_file = try io.getStdErr(); | 53 | stderr_file = try io.getStdErr(); |
| 40 | stderr_file_out_stream = io.FileOutStream.init(&stderr_file); | 54 | stderr_file_out_stream = io.FileOutStream.init(stderr_file); |
| 41 | const st = &stderr_file_out_stream.stream; | 55 | const st = &stderr_file_out_stream.stream; |
| 42 | stderr_stream = st; | 56 | stderr_stream = st; |
| 43 | return st; | 57 | return st; |
| ... | @@ -70,7 +84,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | ... | @@ -70,7 +84,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 70 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | 84 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; |
| 71 | return; | 85 | return; |
| 72 | }; | 86 | }; |
| 73 | writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| { | 87 | writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { |
| 74 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; | 88 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; |
| 75 | return; | 89 | return; |
| 76 | }; | 90 | }; |
| ... | @@ -191,7 +205,11 @@ pub inline fn getReturnAddress(frame_count: usize) usize { | ... | @@ -191,7 +205,11 @@ pub inline fn getReturnAddress(frame_count: usize) usize { |
| 191 | return @intToPtr(*const usize, fp + @sizeOf(usize)).*; | 205 | return @intToPtr(*const usize, fp + @sizeOf(usize)).*; |
| 192 | } | 206 | } |
| 193 | 207 | ||
| 194 | pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { | 208 | pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { |
| 209 | switch (builtin.os) { | ||
| 210 | builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), | ||
| 211 | else => {}, | ||
| 212 | } | ||
| 195 | const AddressState = union(enum) { | 213 | const AddressState = union(enum) { |
| 196 | NotLookingForStartAddress, | 214 | NotLookingForStartAddress, |
| 197 | LookingForStartAddress: usize, | 215 | LookingForStartAddress: usize, |
| ... | @@ -224,18 +242,296 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ | ... | @@ -224,18 +242,296 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ |
| 224 | } | 242 | } |
| 225 | } | 243 | } |
| 226 | 244 | ||
| 245 | pub fn writeCurrentStackTraceWindows(out_stream: var, debug_info: *DebugInfo, | ||
| 246 | tty_color: bool, start_addr: ?usize) !void | ||
| 247 | { | ||
| 248 | var addr_buf: [1024]usize = undefined; | ||
| 249 | const casted_len = @intCast(u32, addr_buf.len); // TODO shouldn't need this cast | ||
| 250 | const n = windows.RtlCaptureStackBackTrace(0, casted_len, @ptrCast(**c_void, &addr_buf), null); | ||
| 251 | const addrs = addr_buf[0..n]; | ||
| 252 | var start_i: usize = if (start_addr) |saddr| blk: { | ||
| 253 | for (addrs) |addr, i| { | ||
| 254 | if (addr == saddr) break :blk i; | ||
| 255 | } | ||
| 256 | return; | ||
| 257 | } else 0; | ||
| 258 | for (addrs[start_i..]) |addr| { | ||
| 259 | try printSourceAtAddress(debug_info, out_stream, addr, tty_color); | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 227 | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | 263 | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { |
| 228 | switch (builtin.os) { | 264 | switch (builtin.os) { |
| 229 | builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), | 265 | builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), |
| 230 | builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), | 266 | builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), |
| 231 | builtin.Os.windows => { | 267 | builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), |
| 232 | // TODO https://github.com/ziglang/zig/issues/721 | ||
| 233 | return error.UnsupportedOperatingSystem; | ||
| 234 | }, | ||
| 235 | else => return error.UnsupportedOperatingSystem, | 268 | else => return error.UnsupportedOperatingSystem, |
| 236 | } | 269 | } |
| 237 | } | 270 | } |
| 238 | 271 | ||
| 272 | fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { | ||
| 273 | const allocator = getDebugInfoAllocator(); | ||
| 274 | const base_address = os.getBaseAddress(); | ||
| 275 | const relative_address = relocated_address - base_address; | ||
| 276 | |||
| 277 | var coff_section: *coff.Section = undefined; | ||
| 278 | const mod_index = for (di.sect_contribs) |sect_contrib| { | ||
| 279 | if (sect_contrib.Section >= di.coff.sections.len) continue; | ||
| 280 | coff_section = &di.coff.sections.toSlice()[sect_contrib.Section]; | ||
| 281 | |||
| 282 | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; | ||
| 283 | const vaddr_end = vaddr_start + sect_contrib.Size; | ||
| 284 | if (relative_address >= vaddr_start and relative_address < vaddr_end) { | ||
| 285 | break sect_contrib.ModuleIndex; | ||
| 286 | } | ||
| 287 | } else { | ||
| 288 | // we have no information to add to the address | ||
| 289 | if (tty_color) { | ||
| 290 | try out_stream.print("???:?:?: "); | ||
| 291 | setTtyColor(TtyColor.Dim); | ||
| 292 | try out_stream.print("0x{x} in ??? (???)", relocated_address); | ||
| 293 | setTtyColor(TtyColor.Reset); | ||
| 294 | try out_stream.print("\n\n\n"); | ||
| 295 | } else { | ||
| 296 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address); | ||
| 297 | } | ||
| 298 | return; | ||
| 299 | }; | ||
| 300 | |||
| 301 | const mod = &di.modules[mod_index]; | ||
| 302 | try populateModule(di, mod); | ||
| 303 | const obj_basename = os.path.basename(mod.obj_file_name); | ||
| 304 | |||
| 305 | var symbol_i: usize = 0; | ||
| 306 | const symbol_name = while (symbol_i != mod.symbols.len) { | ||
| 307 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); | ||
| 308 | if (prefix.RecordLen < 2) | ||
| 309 | return error.InvalidDebugInfo; | ||
| 310 | switch (prefix.RecordKind) { | ||
| 311 | pdb.SymbolKind.S_LPROC32 => { | ||
| 312 | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); | ||
| 313 | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; | ||
| 314 | const vaddr_end = vaddr_start + proc_sym.CodeSize; | ||
| 315 | if (relative_address >= vaddr_start and relative_address < vaddr_end) { | ||
| 316 | break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym)); | ||
| 317 | } | ||
| 318 | }, | ||
| 319 | else => {}, | ||
| 320 | } | ||
| 321 | symbol_i += prefix.RecordLen + @sizeOf(u16); | ||
| 322 | if (symbol_i > mod.symbols.len) | ||
| 323 | return error.InvalidDebugInfo; | ||
| 324 | } else "???"; | ||
| 325 | |||
| 326 | const subsect_info = mod.subsect_info; | ||
| 327 | |||
| 328 | var sect_offset: usize = 0; | ||
| 329 | var skip_len: usize = undefined; | ||
| 330 | const opt_line_info = subsections: { | ||
| 331 | const checksum_offset = mod.checksum_offset orelse break :subsections null; | ||
| 332 | while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { | ||
| 333 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); | ||
| 334 | skip_len = subsect_hdr.Length; | ||
| 335 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | ||
| 336 | |||
| 337 | switch (subsect_hdr.Kind) { | ||
| 338 | pdb.DebugSubsectionKind.Lines => { | ||
| 339 | var line_index: usize = sect_offset; | ||
| 340 | |||
| 341 | const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); | ||
| 342 | if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; | ||
| 343 | line_index += @sizeOf(pdb.LineFragmentHeader); | ||
| 344 | |||
| 345 | const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); | ||
| 346 | line_index += @sizeOf(pdb.LineBlockFragmentHeader); | ||
| 347 | |||
| 348 | const has_column = line_hdr.Flags.LF_HaveColumns; | ||
| 349 | |||
| 350 | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; | ||
| 351 | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; | ||
| 352 | if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { | ||
| 353 | var line_i: usize = 0; | ||
| 354 | const start_line_index = line_index; | ||
| 355 | while (line_i < block_hdr.NumLines) : (line_i += 1) { | ||
| 356 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); | ||
| 357 | line_index += @sizeOf(pdb.LineNumberEntry); | ||
| 358 | const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); | ||
| 359 | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; | ||
| 360 | const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; | ||
| 361 | if (relative_address >= vaddr_start and relative_address < vaddr_end) { | ||
| 362 | const subsect_index = checksum_offset + block_hdr.NameIndex; | ||
| 363 | const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); | ||
| 364 | const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; | ||
| 365 | try di.pdb.string_table.seekTo(strtab_offset); | ||
| 366 | const source_file_name = try di.pdb.string_table.readNullTermString(allocator); | ||
| 367 | const line = flags.Start; | ||
| 368 | const column = if (has_column) blk: { | ||
| 369 | line_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; | ||
| 370 | line_index += @sizeOf(pdb.ColumnNumberEntry) * line_i; | ||
| 371 | const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[line_index]); | ||
| 372 | break :blk col_num_entry.StartColumn; | ||
| 373 | } else 0; | ||
| 374 | break :subsections LineInfo{ | ||
| 375 | .allocator = allocator, | ||
| 376 | .file_name = source_file_name, | ||
| 377 | .line = line, | ||
| 378 | .column = column, | ||
| 379 | }; | ||
| 380 | } | ||
| 381 | } | ||
| 382 | break :subsections null; | ||
| 383 | } | ||
| 384 | }, | ||
| 385 | else => {}, | ||
| 386 | } | ||
| 387 | |||
| 388 | if (sect_offset > subsect_info.len) | ||
| 389 | return error.InvalidDebugInfo; | ||
| 390 | } else { | ||
| 391 | break :subsections null; | ||
| 392 | } | ||
| 393 | }; | ||
| 394 | |||
| 395 | if (tty_color) { | ||
| 396 | setTtyColor(TtyColor.White); | ||
| 397 | if (opt_line_info) |li| { | ||
| 398 | try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column); | ||
| 399 | } else { | ||
| 400 | try out_stream.print("???:?:?"); | ||
| 401 | } | ||
| 402 | setTtyColor(TtyColor.Reset); | ||
| 403 | try out_stream.print(": "); | ||
| 404 | setTtyColor(TtyColor.Dim); | ||
| 405 | try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename); | ||
| 406 | setTtyColor(TtyColor.Reset); | ||
| 407 | |||
| 408 | if (opt_line_info) |line_info| { | ||
| 409 | try out_stream.print("\n"); | ||
| 410 | if (printLineFromFile(out_stream, line_info)) { | ||
| 411 | if (line_info.column == 0) { | ||
| 412 | try out_stream.write("\n"); | ||
| 413 | } else { | ||
| 414 | { | ||
| 415 | var col_i: usize = 1; | ||
| 416 | while (col_i < line_info.column) : (col_i += 1) { | ||
| 417 | try out_stream.writeByte(' '); | ||
| 418 | } | ||
| 419 | } | ||
| 420 | setTtyColor(TtyColor.Green); | ||
| 421 | try out_stream.write("^"); | ||
| 422 | setTtyColor(TtyColor.Reset); | ||
| 423 | try out_stream.write("\n"); | ||
| 424 | } | ||
| 425 | } else |err| switch (err) { | ||
| 426 | error.EndOfFile => {}, | ||
| 427 | else => return err, | ||
| 428 | } | ||
| 429 | } else { | ||
| 430 | try out_stream.print("\n\n\n"); | ||
| 431 | } | ||
| 432 | } else { | ||
| 433 | if (opt_line_info) |li| { | ||
| 434 | try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename); | ||
| 435 | } else { | ||
| 436 | try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename); | ||
| 437 | } | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | const TtyColor = enum{ | ||
| 442 | Red, | ||
| 443 | Green, | ||
| 444 | Cyan, | ||
| 445 | White, | ||
| 446 | Dim, | ||
| 447 | Bold, | ||
| 448 | Reset, | ||
| 449 | }; | ||
| 450 | |||
| 451 | /// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt | ||
| 452 | fn setTtyColor(tty_color: TtyColor) void { | ||
| 453 | const S = struct { | ||
| 454 | var attrs: windows.WORD = undefined; | ||
| 455 | var init_attrs = false; | ||
| 456 | }; | ||
| 457 | if (!S.init_attrs) { | ||
| 458 | S.init_attrs = true; | ||
| 459 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | ||
| 460 | // TODO handle error | ||
| 461 | _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info); | ||
| 462 | S.attrs = info.wAttributes; | ||
| 463 | } | ||
| 464 | |||
| 465 | // TODO handle errors | ||
| 466 | switch (tty_color) { | ||
| 467 | TtyColor.Red => { | ||
| 468 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED|windows.FOREGROUND_INTENSITY); | ||
| 469 | }, | ||
| 470 | TtyColor.Green => { | ||
| 471 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN|windows.FOREGROUND_INTENSITY); | ||
| 472 | }, | ||
| 473 | TtyColor.Cyan => { | ||
| 474 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, | ||
| 475 | windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY); | ||
| 476 | }, | ||
| 477 | TtyColor.White, TtyColor.Bold => { | ||
| 478 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, | ||
| 479 | windows.FOREGROUND_RED|windows.FOREGROUND_GREEN|windows.FOREGROUND_BLUE|windows.FOREGROUND_INTENSITY); | ||
| 480 | }, | ||
| 481 | TtyColor.Dim => { | ||
| 482 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY); | ||
| 483 | }, | ||
| 484 | TtyColor.Reset => { | ||
| 485 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs); | ||
| 486 | }, | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | fn populateModule(di: *DebugInfo, mod: *Module) !void { | ||
| 491 | if (mod.populated) | ||
| 492 | return; | ||
| 493 | const allocator = getDebugInfoAllocator(); | ||
| 494 | |||
| 495 | if (mod.mod_info.C11ByteSize != 0) | ||
| 496 | return error.InvalidDebugInfo; | ||
| 497 | |||
| 498 | if (mod.mod_info.C13ByteSize == 0) | ||
| 499 | return error.MissingDebugInfo; | ||
| 500 | |||
| 501 | const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; | ||
| 502 | |||
| 503 | const signature = try modi.stream.readIntLe(u32); | ||
| 504 | if (signature != 4) | ||
| 505 | return error.InvalidDebugInfo; | ||
| 506 | |||
| 507 | mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4); | ||
| 508 | try modi.stream.readNoEof(mod.symbols); | ||
| 509 | |||
| 510 | mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize); | ||
| 511 | try modi.stream.readNoEof(mod.subsect_info); | ||
| 512 | |||
| 513 | var sect_offset: usize = 0; | ||
| 514 | var skip_len: usize = undefined; | ||
| 515 | while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) { | ||
| 516 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]); | ||
| 517 | skip_len = subsect_hdr.Length; | ||
| 518 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | ||
| 519 | |||
| 520 | switch (subsect_hdr.Kind) { | ||
| 521 | pdb.DebugSubsectionKind.FileChecksums => { | ||
| 522 | mod.checksum_offset = sect_offset; | ||
| 523 | break; | ||
| 524 | }, | ||
| 525 | else => {}, | ||
| 526 | } | ||
| 527 | |||
| 528 | if (sect_offset > mod.subsect_info.len) | ||
| 529 | return error.InvalidDebugInfo; | ||
| 530 | } | ||
| 531 | |||
| 532 | mod.populated = true; | ||
| 533 | } | ||
| 534 | |||
| 239 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { | 535 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { |
| 240 | var min: usize = 0; | 536 | var min: usize = 0; |
| 241 | var max: usize = symbols.len - 1; // Exclude sentinel. | 537 | var max: usize = symbols.len - 1; // Exclude sentinel. |
| ... | @@ -372,14 +668,185 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -372,14 +668,185 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { |
| 372 | switch (builtin.os) { | 668 | switch (builtin.os) { |
| 373 | builtin.Os.linux => return openSelfDebugInfoLinux(allocator), | 669 | builtin.Os.linux => return openSelfDebugInfoLinux(allocator), |
| 374 | builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), | 670 | builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), |
| 375 | builtin.Os.windows => { | 671 | builtin.Os.windows => return openSelfDebugInfoWindows(allocator), |
| 376 | // TODO: https://github.com/ziglang/zig/issues/721 | ||
| 377 | return error.UnsupportedOperatingSystem; | ||
| 378 | }, | ||
| 379 | else => return error.UnsupportedOperatingSystem, | 672 | else => return error.UnsupportedOperatingSystem, |
| 380 | } | 673 | } |
| 381 | } | 674 | } |
| 382 | 675 | ||
| 676 | fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { | ||
| 677 | const self_file = try os.openSelfExe(); | ||
| 678 | defer self_file.close(); | ||
| 679 | |||
| 680 | const coff_obj = try allocator.createOne(coff.Coff); | ||
| 681 | coff_obj.* = coff.Coff{ | ||
| 682 | .in_file = self_file, | ||
| 683 | .allocator = allocator, | ||
| 684 | .coff_header = undefined, | ||
| 685 | .pe_header = undefined, | ||
| 686 | .sections = undefined, | ||
| 687 | .guid = undefined, | ||
| 688 | .age = undefined, | ||
| 689 | }; | ||
| 690 | |||
| 691 | var di = DebugInfo{ | ||
| 692 | .coff = coff_obj, | ||
| 693 | .pdb = undefined, | ||
| 694 | .sect_contribs = undefined, | ||
| 695 | .modules = undefined, | ||
| 696 | }; | ||
| 697 | |||
| 698 | try di.coff.loadHeader(); | ||
| 699 | |||
| 700 | var path_buf: [windows.MAX_PATH]u8 = undefined; | ||
| 701 | const len = try di.coff.getPdbPath(path_buf[0..]); | ||
| 702 | const raw_path = path_buf[0..len]; | ||
| 703 | |||
| 704 | const path = try os.path.resolve(allocator, raw_path); | ||
| 705 | |||
| 706 | try di.pdb.openFile(di.coff, path); | ||
| 707 | |||
| 708 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | ||
| 709 | const version = try pdb_stream.stream.readIntLe(u32); | ||
| 710 | const signature = try pdb_stream.stream.readIntLe(u32); | ||
| 711 | const age = try pdb_stream.stream.readIntLe(u32); | ||
| 712 | var guid: [16]u8 = undefined; | ||
| 713 | try pdb_stream.stream.readNoEof(guid[0..]); | ||
| 714 | if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) | ||
| 715 | return error.InvalidDebugInfo; | ||
| 716 | // We validated the executable and pdb match. | ||
| 717 | |||
| 718 | const string_table_index = str_tab_index: { | ||
| 719 | const name_bytes_len = try pdb_stream.stream.readIntLe(u32); | ||
| 720 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | ||
| 721 | try pdb_stream.stream.readNoEof(name_bytes); | ||
| 722 | |||
| 723 | const HashTableHeader = packed struct { | ||
| 724 | Size: u32, | ||
| 725 | Capacity: u32, | ||
| 726 | |||
| 727 | fn maxLoad(cap: u32) u32 { | ||
| 728 | return cap * 2 / 3 + 1; | ||
| 729 | } | ||
| 730 | }; | ||
| 731 | var hash_tbl_hdr: HashTableHeader = undefined; | ||
| 732 | try pdb_stream.stream.readStruct(HashTableHeader, &hash_tbl_hdr); | ||
| 733 | if (hash_tbl_hdr.Capacity == 0) | ||
| 734 | return error.InvalidDebugInfo; | ||
| 735 | |||
| 736 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | ||
| 737 | return error.InvalidDebugInfo; | ||
| 738 | |||
| 739 | const present = try readSparseBitVector(&pdb_stream.stream, allocator); | ||
| 740 | if (present.len != hash_tbl_hdr.Size) | ||
| 741 | return error.InvalidDebugInfo; | ||
| 742 | const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); | ||
| 743 | |||
| 744 | const Bucket = struct { | ||
| 745 | first: u32, | ||
| 746 | second: u32, | ||
| 747 | }; | ||
| 748 | const bucket_list = try allocator.alloc(Bucket, present.len); | ||
| 749 | for (present) |_| { | ||
| 750 | const name_offset = try pdb_stream.stream.readIntLe(u32); | ||
| 751 | const name_index = try pdb_stream.stream.readIntLe(u32); | ||
| 752 | const name = mem.toSlice(u8, name_bytes.ptr + name_offset); | ||
| 753 | if (mem.eql(u8, name, "/names")) { | ||
| 754 | break :str_tab_index name_index; | ||
| 755 | } | ||
| 756 | } | ||
| 757 | return error.MissingDebugInfo; | ||
| 758 | }; | ||
| 759 | |||
| 760 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; | ||
| 761 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | ||
| 762 | |||
| 763 | const dbi = di.pdb.dbi; | ||
| 764 | |||
| 765 | // Dbi Header | ||
| 766 | var dbi_stream_header: pdb.DbiStreamHeader = undefined; | ||
| 767 | try dbi.stream.readStruct(pdb.DbiStreamHeader, &dbi_stream_header); | ||
| 768 | const mod_info_size = dbi_stream_header.ModInfoSize; | ||
| 769 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | ||
| 770 | |||
| 771 | var modules = ArrayList(Module).init(allocator); | ||
| 772 | |||
| 773 | // Module Info Substream | ||
| 774 | var mod_info_offset: usize = 0; | ||
| 775 | while (mod_info_offset != mod_info_size) { | ||
| 776 | var mod_info: pdb.ModInfo = undefined; | ||
| 777 | try dbi.stream.readStruct(pdb.ModInfo, &mod_info); | ||
| 778 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | ||
| 779 | |||
| 780 | const module_name = try dbi.readNullTermString(allocator); | ||
| 781 | this_record_len += module_name.len + 1; | ||
| 782 | |||
| 783 | const obj_file_name = try dbi.readNullTermString(allocator); | ||
| 784 | this_record_len += obj_file_name.len + 1; | ||
| 785 | |||
| 786 | const march_forward_bytes = this_record_len % 4; | ||
| 787 | if (march_forward_bytes != 0) { | ||
| 788 | try dbi.seekForward(march_forward_bytes); | ||
| 789 | this_record_len += march_forward_bytes; | ||
| 790 | } | ||
| 791 | |||
| 792 | try modules.append(Module{ | ||
| 793 | .mod_info = mod_info, | ||
| 794 | .module_name = module_name, | ||
| 795 | .obj_file_name = obj_file_name, | ||
| 796 | |||
| 797 | .populated = false, | ||
| 798 | .symbols = undefined, | ||
| 799 | .subsect_info = undefined, | ||
| 800 | .checksum_offset = null, | ||
| 801 | }); | ||
| 802 | |||
| 803 | mod_info_offset += this_record_len; | ||
| 804 | if (mod_info_offset > mod_info_size) | ||
| 805 | return error.InvalidDebugInfo; | ||
| 806 | } | ||
| 807 | |||
| 808 | di.modules = modules.toOwnedSlice(); | ||
| 809 | |||
| 810 | // Section Contribution Substream | ||
| 811 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | ||
| 812 | var sect_cont_offset: usize = 0; | ||
| 813 | if (section_contrib_size != 0) { | ||
| 814 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLe(u32)); | ||
| 815 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | ||
| 816 | return error.InvalidDebugInfo; | ||
| 817 | sect_cont_offset += @sizeOf(u32); | ||
| 818 | } | ||
| 819 | while (sect_cont_offset != section_contrib_size) { | ||
| 820 | const entry = try sect_contribs.addOne(); | ||
| 821 | try dbi.stream.readStruct(pdb.SectionContribEntry, entry); | ||
| 822 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | ||
| 823 | |||
| 824 | if (sect_cont_offset > section_contrib_size) | ||
| 825 | return error.InvalidDebugInfo; | ||
| 826 | } | ||
| 827 | |||
| 828 | di.sect_contribs = sect_contribs.toOwnedSlice(); | ||
| 829 | |||
| 830 | return di; | ||
| 831 | } | ||
| 832 | |||
| 833 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { | ||
| 834 | const num_words = try stream.readIntLe(u32); | ||
| 835 | var word_i: usize = 0; | ||
| 836 | var list = ArrayList(usize).init(allocator); | ||
| 837 | while (word_i != num_words) : (word_i += 1) { | ||
| 838 | const word = try stream.readIntLe(u32); | ||
| 839 | var bit_i: u5 = 0; | ||
| 840 | while (true) : (bit_i += 1) { | ||
| 841 | if (word & (u32(1) << bit_i) != 0) { | ||
| 842 | try list.append(word_i * 32 + bit_i); | ||
| 843 | } | ||
| 844 | if (bit_i == @maxValue(u5)) break; | ||
| 845 | } | ||
| 846 | } | ||
| 847 | return list.toOwnedSlice(); | ||
| 848 | } | ||
| 849 | |||
| 383 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { | 850 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { |
| 384 | var di = DebugInfo{ | 851 | var di = DebugInfo{ |
| 385 | .self_exe_file = undefined, | 852 | .self_exe_file = undefined, |
| ... | @@ -395,7 +862,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -395,7 +862,7 @@ fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo { |
| 395 | di.self_exe_file = try os.openSelfExe(); | 862 | di.self_exe_file = try os.openSelfExe(); |
| 396 | errdefer di.self_exe_file.close(); | 863 | errdefer di.self_exe_file.close(); |
| 397 | 864 | ||
| 398 | try di.elf.openFile(allocator, &di.self_exe_file); | 865 | try di.elf.openFile(allocator, di.self_exe_file); |
| 399 | errdefer di.elf.close(); | 866 | errdefer di.elf.close(); |
| 400 | 867 | ||
| 401 | di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo; | 868 | di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo; |
| ... | @@ -578,7 +1045,13 @@ pub const DebugInfo = switch (builtin.os) { | ... | @@ -578,7 +1045,13 @@ pub const DebugInfo = switch (builtin.os) { |
| 578 | return self.ofiles.allocator; | 1045 | return self.ofiles.allocator; |
| 579 | } | 1046 | } |
| 580 | }, | 1047 | }, |
| 581 | else => struct { | 1048 | builtin.Os.windows => struct { |
| 1049 | pdb: pdb.Pdb, | ||
| 1050 | coff: *coff.Coff, | ||
| 1051 | sect_contribs: []pdb.SectionContribEntry, | ||
| 1052 | modules: []Module, | ||
| 1053 | }, | ||
| 1054 | builtin.Os.linux => struct { | ||
| 582 | self_exe_file: os.File, | 1055 | self_exe_file: os.File, |
| 583 | elf: elf.Elf, | 1056 | elf: elf.Elf, |
| 584 | debug_info: *elf.SectionHeader, | 1057 | debug_info: *elf.SectionHeader, |
| ... | @@ -594,7 +1067,7 @@ pub const DebugInfo = switch (builtin.os) { | ... | @@ -594,7 +1067,7 @@ pub const DebugInfo = switch (builtin.os) { |
| 594 | } | 1067 | } |
| 595 | 1068 | ||
| 596 | pub fn readString(self: *DebugInfo) ![]u8 { | 1069 | pub fn readString(self: *DebugInfo) ![]u8 { |
| 597 | var in_file_stream = io.FileInStream.init(&self.self_exe_file); | 1070 | var in_file_stream = io.FileInStream.init(self.self_exe_file); |
| 598 | const in_stream = &in_file_stream.stream; | 1071 | const in_stream = &in_file_stream.stream; |
| 599 | return readStringRaw(self.allocator(), in_stream); | 1072 | return readStringRaw(self.allocator(), in_stream); |
| 600 | } | 1073 | } |
| ... | @@ -604,6 +1077,7 @@ pub const DebugInfo = switch (builtin.os) { | ... | @@ -604,6 +1077,7 @@ pub const DebugInfo = switch (builtin.os) { |
| 604 | self.elf.close(); | 1077 | self.elf.close(); |
| 605 | } | 1078 | } |
| 606 | }, | 1079 | }, |
| 1080 | else => @compileError("Unsupported OS"), | ||
| 607 | }; | 1081 | }; |
| 608 | 1082 | ||
| 609 | const PcRange = struct { | 1083 | const PcRange = struct { |
| ... | @@ -929,7 +1403,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 | ... | @@ -929,7 +1403,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64 |
| 929 | } | 1403 | } |
| 930 | 1404 | ||
| 931 | fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable { | 1405 | fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable { |
| 932 | const in_file = &st.self_exe_file; | 1406 | const in_file = st.self_exe_file; |
| 933 | var in_file_stream = io.FileInStream.init(in_file); | 1407 | var in_file_stream = io.FileInStream.init(in_file); |
| 934 | const in_stream = &in_file_stream.stream; | 1408 | const in_stream = &in_file_stream.stream; |
| 935 | var result = AbbrevTable.init(st.allocator()); | 1409 | var result = AbbrevTable.init(st.allocator()); |
| ... | @@ -980,7 +1454,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con | ... | @@ -980,7 +1454,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con |
| 980 | } | 1454 | } |
| 981 | 1455 | ||
| 982 | fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { | 1456 | fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { |
| 983 | const in_file = &st.self_exe_file; | 1457 | const in_file = st.self_exe_file; |
| 984 | var in_file_stream = io.FileInStream.init(in_file); | 1458 | var in_file_stream = io.FileInStream.init(in_file); |
| 985 | const in_stream = &in_file_stream.stream; | 1459 | const in_stream = &in_file_stream.stream; |
| 986 | const abbrev_code = try readULeb128(in_stream); | 1460 | const abbrev_code = try readULeb128(in_stream); |
| ... | @@ -1202,7 +1676,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u | ... | @@ -1202,7 +1676,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u |
| 1202 | fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo { | 1676 | fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo { |
| 1203 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); | 1677 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); |
| 1204 | 1678 | ||
| 1205 | const in_file = &di.self_exe_file; | 1679 | const in_file = di.self_exe_file; |
| 1206 | const debug_line_end = di.debug_line.offset + di.debug_line.size; | 1680 | const debug_line_end = di.debug_line.offset + di.debug_line.size; |
| 1207 | var this_offset = di.debug_line.offset; | 1681 | var this_offset = di.debug_line.offset; |
| 1208 | var this_index: usize = 0; | 1682 | var this_index: usize = 0; |
| ... | @@ -1382,7 +1856,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { | ... | @@ -1382,7 +1856,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { |
| 1382 | var this_unit_offset = st.debug_info.offset; | 1856 | var this_unit_offset = st.debug_info.offset; |
| 1383 | var cu_index: usize = 0; | 1857 | var cu_index: usize = 0; |
| 1384 | 1858 | ||
| 1385 | var in_file_stream = io.FileInStream.init(&st.self_exe_file); | 1859 | var in_file_stream = io.FileInStream.init(st.self_exe_file); |
| 1386 | const in_stream = &in_file_stream.stream; | 1860 | const in_stream = &in_file_stream.stream; |
| 1387 | 1861 | ||
| 1388 | while (this_unit_offset < debug_info_end) { | 1862 | while (this_unit_offset < debug_info_end) { |
| ... | @@ -1448,7 +1922,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { | ... | @@ -1448,7 +1922,7 @@ fn scanAllCompileUnits(st: *DebugInfo) !void { |
| 1448 | } | 1922 | } |
| 1449 | 1923 | ||
| 1450 | fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit { | 1924 | fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit { |
| 1451 | var in_file_stream = io.FileInStream.init(&st.self_exe_file); | 1925 | var in_file_stream = io.FileInStream.init(st.self_exe_file); |
| 1452 | const in_stream = &in_file_stream.stream; | 1926 | const in_stream = &in_file_stream.stream; |
| 1453 | for (st.compile_unit_list.toSlice()) |*compile_unit| { | 1927 | for (st.compile_unit_list.toSlice()) |*compile_unit| { |
| 1454 | if (compile_unit.pc_range) |range| { | 1928 | if (compile_unit.pc_range) |range| { |
std/elf.zig+2-2| ... | @@ -353,7 +353,7 @@ pub const SectionHeader = struct { | ... | @@ -353,7 +353,7 @@ pub const SectionHeader = struct { |
| 353 | }; | 353 | }; |
| 354 | 354 | ||
| 355 | pub const Elf = struct { | 355 | pub const Elf = struct { |
| 356 | in_file: *os.File, | 356 | in_file: os.File, |
| 357 | auto_close_stream: bool, | 357 | auto_close_stream: bool, |
| 358 | is_64: bool, | 358 | is_64: bool, |
| 359 | endian: builtin.Endian, | 359 | endian: builtin.Endian, |
| ... | @@ -376,7 +376,7 @@ pub const Elf = struct { | ... | @@ -376,7 +376,7 @@ pub const Elf = struct { |
| 376 | } | 376 | } |
| 377 | 377 | ||
| 378 | /// Call close when done. | 378 | /// Call close when done. |
| 379 | pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: *os.File) !void { | 379 | pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: os.File) !void { |
| 380 | elf.allocator = allocator; | 380 | elf.allocator = allocator; |
| 381 | elf.in_file = file; | 381 | elf.in_file = file; |
| 382 | elf.auto_close_stream = false; | 382 | elf.auto_close_stream = false; |
std/event/lock.zig+1-1| ... | @@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder; | ... | @@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder; |
| 7 | const Loop = std.event.Loop; | 7 | const Loop = std.event.Loop; |
| 8 | 8 | ||
| 9 | /// Thread-safe async/await lock. | 9 | /// Thread-safe async/await lock. |
| 10 | /// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and | 10 | /// coroutines which are waiting for the lock are suspended, and |
| 11 | /// are resumed when the lock is released, in order. | 11 | /// are resumed when the lock is released, in order. |
| 12 | /// Allows only one actor to hold the lock. | 12 | /// Allows only one actor to hold the lock. |
| 13 | pub const Lock = struct { | 13 | pub const Lock = struct { |
std/event/locked.zig+1-1| ... | @@ -3,7 +3,7 @@ const Lock = std.event.Lock; | ... | @@ -3,7 +3,7 @@ const Lock = std.event.Lock; |
| 3 | const Loop = std.event.Loop; | 3 | const Loop = std.event.Loop; |
| 4 | 4 | ||
| 5 | /// Thread-safe async/await lock that protects one piece of data. | 5 | /// Thread-safe async/await lock that protects one piece of data. |
| 6 | /// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and | 6 | /// coroutines which are waiting for the lock are suspended, and |
| 7 | /// are resumed when the lock is released, in order. | 7 | /// are resumed when the lock is released, in order. |
| 8 | pub fn Locked(comptime T: type) type { | 8 | pub fn Locked(comptime T: type) type { |
| 9 | return struct { | 9 | return struct { |
std/event/rwlock.zig+1-1| ... | @@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder; | ... | @@ -7,7 +7,7 @@ const AtomicOrder = builtin.AtomicOrder; |
| 7 | const Loop = std.event.Loop; | 7 | const Loop = std.event.Loop; |
| 8 | 8 | ||
| 9 | /// Thread-safe async/await lock. | 9 | /// Thread-safe async/await lock. |
| 10 | /// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and | 10 | /// coroutines which are waiting for the lock are suspended, and |
| 11 | /// are resumed when the lock is released, in order. | 11 | /// are resumed when the lock is released, in order. |
| 12 | /// Many readers can hold the lock at the same time; however locking for writing is exclusive. | 12 | /// Many readers can hold the lock at the same time; however locking for writing is exclusive. |
| 13 | /// When a read lock is held, it will not be released until the reader queue is empty. | 13 | /// When a read lock is held, it will not be released until the reader queue is empty. |
std/event/rwlocked.zig+1-1| ... | @@ -3,7 +3,7 @@ const RwLock = std.event.RwLock; | ... | @@ -3,7 +3,7 @@ const RwLock = std.event.RwLock; |
| 3 | const Loop = std.event.Loop; | 3 | const Loop = std.event.Loop; |
| 4 | 4 | ||
| 5 | /// Thread-safe async/await RW lock that protects one piece of data. | 5 | /// Thread-safe async/await RW lock that protects one piece of data. |
| 6 | /// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and | 6 | /// coroutines which are waiting for the lock are suspended, and |
| 7 | /// are resumed when the lock is released, in order. | 7 | /// are resumed when the lock is released, in order. |
| 8 | pub fn RwLocked(comptime T: type) type { | 8 | pub fn RwLocked(comptime T: type) type { |
| 9 | return struct { | 9 | return struct { |
std/event/tcp.zig+4-4| ... | @@ -89,7 +89,7 @@ pub const Server = struct { | ... | @@ -89,7 +89,7 @@ pub const Server = struct { |
| 89 | error.ProcessFdQuotaExceeded => { | 89 | error.ProcessFdQuotaExceeded => { |
| 90 | errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); | 90 | errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); |
| 91 | suspend { | 91 | suspend { |
| 92 | self.waiting_for_emfile_node = PromiseNode.init( @handle() ); | 92 | self.waiting_for_emfile_node = PromiseNode.init(@handle()); |
| 93 | std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node); | 93 | std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node); |
| 94 | } | 94 | } |
| 95 | continue; | 95 | continue; |
| ... | @@ -145,11 +145,11 @@ test "listen on a port, send bytes, receive bytes" { | ... | @@ -145,11 +145,11 @@ test "listen on a port, send bytes, receive bytes" { |
| 145 | cancel @handle(); | 145 | cancel @handle(); |
| 146 | } | 146 | } |
| 147 | } | 147 | } |
| 148 | async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { | 148 | async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: std.os.File) !void { |
| 149 | const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733 | 149 | const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733 |
| 150 | var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733 | 150 | var socket = _socket; // TODO https://github.com/ziglang/zig/issues/733 |
| 151 | 151 | ||
| 152 | var adapter = std.io.FileOutStream.init(&socket); | 152 | var adapter = std.io.FileOutStream.init(socket); |
| 153 | var stream = &adapter.stream; | 153 | var stream = &adapter.stream; |
| 154 | try stream.print("hello from server\n"); | 154 | try stream.print("hello from server\n"); |
| 155 | } | 155 | } |
std/fmt/index.zig+126-17| ... | @@ -163,26 +163,47 @@ pub fn formatType( | ... | @@ -163,26 +163,47 @@ pub fn formatType( |
| 163 | } | 163 | } |
| 164 | break :cf false; | 164 | break :cf false; |
| 165 | }; | 165 | }; |
| 166 | |||
| 167 | if (has_cust_fmt) return value.format(fmt, context, Errors, output); | 166 | if (has_cust_fmt) return value.format(fmt, context, Errors, output); |
| 167 | |||
| 168 | try output(context, @typeName(T)); | 168 | try output(context, @typeName(T)); |
| 169 | if (comptime @typeId(T) == builtin.TypeId.Enum) { | 169 | switch (comptime @typeId(T)) { |
| 170 | try output(context, "."); | 170 | builtin.TypeId.Enum => { |
| 171 | try formatType(@tagName(value), "", context, Errors, output); | 171 | try output(context, "."); |
| 172 | return; | 172 | try formatType(@tagName(value), "", context, Errors, output); |
| 173 | } | 173 | return; |
| 174 | comptime var field_i = 0; | 174 | }, |
| 175 | inline while (field_i < @memberCount(T)) : (field_i += 1) { | 175 | builtin.TypeId.Struct => { |
| 176 | if (field_i == 0) { | 176 | comptime var field_i = 0; |
| 177 | try output(context, "{ ."); | 177 | inline while (field_i < @memberCount(T)) : (field_i += 1) { |
| 178 | } else { | 178 | if (field_i == 0) { |
| 179 | try output(context, ", ."); | 179 | try output(context, "{ ."); |
| 180 | } | 180 | } else { |
| 181 | try output(context, @memberName(T, field_i)); | 181 | try output(context, ", ."); |
| 182 | try output(context, " = "); | 182 | } |
| 183 | try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output); | 183 | try output(context, @memberName(T, field_i)); |
| 184 | try output(context, " = "); | ||
| 185 | try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output); | ||
| 186 | } | ||
| 187 | try output(context, " }"); | ||
| 188 | }, | ||
| 189 | builtin.TypeId.Union => { | ||
| 190 | const info = @typeInfo(T).Union; | ||
| 191 | if (info.tag_type) |UnionTagType| { | ||
| 192 | try output(context, "{ ."); | ||
| 193 | try output(context, @tagName(UnionTagType(value))); | ||
| 194 | try output(context, " = "); | ||
| 195 | inline for (info.fields) |u_field| { | ||
| 196 | if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { | ||
| 197 | try formatType(@field(value, u_field.name), "", context, Errors, output); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | try output(context, " }"); | ||
| 201 | } else { | ||
| 202 | try format(context, Errors, output, "@{x}", @ptrToInt(&value)); | ||
| 203 | } | ||
| 204 | }, | ||
| 205 | else => unreachable, | ||
| 184 | } | 206 | } |
| 185 | try output(context, " }"); | ||
| 186 | return; | 207 | return; |
| 187 | }, | 208 | }, |
| 188 | builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { | 209 | builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { |
| ... | @@ -329,6 +350,11 @@ pub fn formatText( | ... | @@ -329,6 +350,11 @@ pub fn formatText( |
| 329 | comptime var width = 0; | 350 | comptime var width = 0; |
| 330 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | 351 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); |
| 331 | return formatBuf(bytes, width, context, Errors, output); | 352 | return formatBuf(bytes, width, context, Errors, output); |
| 353 | } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) { | ||
| 354 | for (bytes) |c| { | ||
| 355 | try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); | ||
| 356 | } | ||
| 357 | return; | ||
| 332 | } else @compileError("Unknown format character: " ++ []u8{fmt[0]}); | 358 | } else @compileError("Unknown format character: " ++ []u8{fmt[0]}); |
| 333 | } | 359 | } |
| 334 | return output(context, bytes); | 360 | return output(context, bytes); |
| ... | @@ -1194,6 +1220,70 @@ test "fmt.format" { | ... | @@ -1194,6 +1220,70 @@ test "fmt.format" { |
| 1194 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); | 1220 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); |
| 1195 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); | 1221 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); |
| 1196 | } | 1222 | } |
| 1223 | //struct format | ||
| 1224 | { | ||
| 1225 | const S = struct { | ||
| 1226 | a: u32, | ||
| 1227 | b: error, | ||
| 1228 | }; | ||
| 1229 | |||
| 1230 | const inst = S{ | ||
| 1231 | .a = 456, | ||
| 1232 | .b = error.Unused, | ||
| 1233 | }; | ||
| 1234 | |||
| 1235 | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); | ||
| 1236 | } | ||
| 1237 | //union format | ||
| 1238 | { | ||
| 1239 | const TU = union(enum) { | ||
| 1240 | float: f32, | ||
| 1241 | int: u32, | ||
| 1242 | }; | ||
| 1243 | |||
| 1244 | const UU = union { | ||
| 1245 | float: f32, | ||
| 1246 | int: u32, | ||
| 1247 | }; | ||
| 1248 | |||
| 1249 | const EU = extern union { | ||
| 1250 | float: f32, | ||
| 1251 | int: u32, | ||
| 1252 | }; | ||
| 1253 | |||
| 1254 | const tu_inst = TU{ .int = 123 }; | ||
| 1255 | const uu_inst = UU{ .int = 456 }; | ||
| 1256 | const eu_inst = EU{ .float = 321.123 }; | ||
| 1257 | |||
| 1258 | try testFmt("TU{ .int = 123 }", "{}", tu_inst); | ||
| 1259 | |||
| 1260 | var buf: [100]u8 = undefined; | ||
| 1261 | const uu_result = try bufPrint(buf[0..], "{}", uu_inst); | ||
| 1262 | debug.assert(mem.eql(u8, uu_result[0..3], "UU@")); | ||
| 1263 | |||
| 1264 | const eu_result = try bufPrint(buf[0..], "{}", eu_inst); | ||
| 1265 | debug.assert(mem.eql(u8, uu_result[0..3], "EU@")); | ||
| 1266 | } | ||
| 1267 | //enum format | ||
| 1268 | { | ||
| 1269 | const E = enum { | ||
| 1270 | One, | ||
| 1271 | Two, | ||
| 1272 | Three, | ||
| 1273 | }; | ||
| 1274 | |||
| 1275 | const inst = E.Two; | ||
| 1276 | |||
| 1277 | try testFmt("E.Two", "{}", inst); | ||
| 1278 | } | ||
| 1279 | //print bytes as hex | ||
| 1280 | { | ||
| 1281 | const some_bytes = "\xCA\xFE\xBA\xBE"; | ||
| 1282 | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); | ||
| 1283 | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); | ||
| 1284 | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; | ||
| 1285 | try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); | ||
| 1286 | } | ||
| 1197 | } | 1287 | } |
| 1198 | 1288 | ||
| 1199 | fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { | 1289 | fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { |
| ... | @@ -1241,3 +1331,22 @@ pub fn isWhiteSpace(byte: u8) bool { | ... | @@ -1241,3 +1331,22 @@ pub fn isWhiteSpace(byte: u8) bool { |
| 1241 | else => false, | 1331 | else => false, |
| 1242 | }; | 1332 | }; |
| 1243 | } | 1333 | } |
| 1334 | |||
| 1335 | pub fn hexToBytes(out: []u8, input: []const u8) !void { | ||
| 1336 | if (out.len * 2 < input.len) | ||
| 1337 | return error.InvalidLength; | ||
| 1338 | |||
| 1339 | var in_i: usize = 0; | ||
| 1340 | while (in_i != input.len) : (in_i += 2) { | ||
| 1341 | const hi = try charToDigit(input[in_i], 16); | ||
| 1342 | const lo = try charToDigit(input[in_i + 1], 16); | ||
| 1343 | out[in_i / 2] = (hi << 4) | lo; | ||
| 1344 | } | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | test "fmt.hexToBytes" { | ||
| 1348 | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; | ||
| 1349 | var pb: [32]u8 = undefined; | ||
| 1350 | try hexToBytes(pb[0..], test_hex_str); | ||
| 1351 | try testFmt(test_hex_str, "{X}", pb); | ||
| 1352 | } |
std/index.zig+4| ... | @@ -15,6 +15,7 @@ pub const atomic = @import("atomic/index.zig"); | ... | @@ -15,6 +15,7 @@ pub const atomic = @import("atomic/index.zig"); |
| 15 | pub const base64 = @import("base64.zig"); | 15 | pub const base64 = @import("base64.zig"); |
| 16 | pub const build = @import("build.zig"); | 16 | pub const build = @import("build.zig"); |
| 17 | pub const c = @import("c/index.zig"); | 17 | pub const c = @import("c/index.zig"); |
| 18 | pub const coff = @import("coff.zig"); | ||
| 18 | pub const crypto = @import("crypto/index.zig"); | 19 | pub const crypto = @import("crypto/index.zig"); |
| 19 | pub const cstr = @import("cstr.zig"); | 20 | pub const cstr = @import("cstr.zig"); |
| 20 | pub const debug = @import("debug/index.zig"); | 21 | pub const debug = @import("debug/index.zig"); |
| ... | @@ -33,6 +34,7 @@ pub const math = @import("math/index.zig"); | ... | @@ -33,6 +34,7 @@ pub const math = @import("math/index.zig"); |
| 33 | pub const mem = @import("mem.zig"); | 34 | pub const mem = @import("mem.zig"); |
| 34 | pub const net = @import("net.zig"); | 35 | pub const net = @import("net.zig"); |
| 35 | pub const os = @import("os/index.zig"); | 36 | pub const os = @import("os/index.zig"); |
| 37 | pub const pdb = @import("pdb.zig"); | ||
| 36 | pub const rand = @import("rand/index.zig"); | 38 | pub const rand = @import("rand/index.zig"); |
| 37 | pub const rb = @import("rb.zig"); | 39 | pub const rb = @import("rb.zig"); |
| 38 | pub const sort = @import("sort.zig"); | 40 | pub const sort = @import("sort.zig"); |
| ... | @@ -56,6 +58,7 @@ test "std" { | ... | @@ -56,6 +58,7 @@ test "std" { |
| 56 | _ = @import("base64.zig"); | 58 | _ = @import("base64.zig"); |
| 57 | _ = @import("build.zig"); | 59 | _ = @import("build.zig"); |
| 58 | _ = @import("c/index.zig"); | 60 | _ = @import("c/index.zig"); |
| 61 | _ = @import("coff.zig"); | ||
| 59 | _ = @import("crypto/index.zig"); | 62 | _ = @import("crypto/index.zig"); |
| 60 | _ = @import("cstr.zig"); | 63 | _ = @import("cstr.zig"); |
| 61 | _ = @import("debug/index.zig"); | 64 | _ = @import("debug/index.zig"); |
| ... | @@ -74,6 +77,7 @@ test "std" { | ... | @@ -74,6 +77,7 @@ test "std" { |
| 74 | _ = @import("heap.zig"); | 77 | _ = @import("heap.zig"); |
| 75 | _ = @import("os/index.zig"); | 78 | _ = @import("os/index.zig"); |
| 76 | _ = @import("rand/index.zig"); | 79 | _ = @import("rand/index.zig"); |
| 80 | _ = @import("pdb.zig"); | ||
| 77 | _ = @import("sort.zig"); | 81 | _ = @import("sort.zig"); |
| 78 | _ = @import("unicode.zig"); | 82 | _ = @import("unicode.zig"); |
| 79 | _ = @import("zig/index.zig"); | 83 | _ = @import("zig/index.zig"); |
std/io.zig+8-8| ... | @@ -34,13 +34,13 @@ pub fn getStdIn() GetStdIoErrs!File { | ... | @@ -34,13 +34,13 @@ pub fn getStdIn() GetStdIoErrs!File { |
| 34 | 34 | ||
| 35 | /// Implementation of InStream trait for File | 35 | /// Implementation of InStream trait for File |
| 36 | pub const FileInStream = struct { | 36 | pub const FileInStream = struct { |
| 37 | file: *File, | 37 | file: File, |
| 38 | stream: Stream, | 38 | stream: Stream, |
| 39 | 39 | ||
| 40 | pub const Error = @typeOf(File.read).ReturnType.ErrorSet; | 40 | pub const Error = @typeOf(File.read).ReturnType.ErrorSet; |
| 41 | pub const Stream = InStream(Error); | 41 | pub const Stream = InStream(Error); |
| 42 | 42 | ||
| 43 | pub fn init(file: *File) FileInStream { | 43 | pub fn init(file: File) FileInStream { |
| 44 | return FileInStream{ | 44 | return FileInStream{ |
| 45 | .file = file, | 45 | .file = file, |
| 46 | .stream = Stream{ .readFn = readFn }, | 46 | .stream = Stream{ .readFn = readFn }, |
| ... | @@ -55,13 +55,13 @@ pub const FileInStream = struct { | ... | @@ -55,13 +55,13 @@ pub const FileInStream = struct { |
| 55 | 55 | ||
| 56 | /// Implementation of OutStream trait for File | 56 | /// Implementation of OutStream trait for File |
| 57 | pub const FileOutStream = struct { | 57 | pub const FileOutStream = struct { |
| 58 | file: *File, | 58 | file: File, |
| 59 | stream: Stream, | 59 | stream: Stream, |
| 60 | 60 | ||
| 61 | pub const Error = File.WriteError; | 61 | pub const Error = File.WriteError; |
| 62 | pub const Stream = OutStream(Error); | 62 | pub const Stream = OutStream(Error); |
| 63 | 63 | ||
| 64 | pub fn init(file: *File) FileOutStream { | 64 | pub fn init(file: File) FileOutStream { |
| 65 | return FileOutStream{ | 65 | return FileOutStream{ |
| 66 | .file = file, | 66 | .file = file, |
| 67 | .stream = Stream{ .writeFn = writeFn }, | 67 | .stream = Stream{ .writeFn = writeFn }, |
| ... | @@ -210,7 +210,7 @@ pub fn InStream(comptime ReadError: type) type { | ... | @@ -210,7 +210,7 @@ pub fn InStream(comptime ReadError: type) type { |
| 210 | 210 | ||
| 211 | pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void { | 211 | pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void { |
| 212 | // Only extern and packed structs have defined in-memory layout. | 212 | // Only extern and packed structs have defined in-memory layout. |
| 213 | assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto); | 213 | comptime assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto); |
| 214 | return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..])); | 214 | return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..])); |
| 215 | } | 215 | } |
| 216 | }; | 216 | }; |
| ... | @@ -280,7 +280,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim | ... | @@ -280,7 +280,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim |
| 280 | const buf = try allocator.alignedAlloc(u8, A, size); | 280 | const buf = try allocator.alignedAlloc(u8, A, size); |
| 281 | errdefer allocator.free(buf); | 281 | errdefer allocator.free(buf); |
| 282 | 282 | ||
| 283 | var adapter = FileInStream.init(&file); | 283 | var adapter = FileInStream.init(file); |
| 284 | try adapter.stream.readNoEof(buf[0..size]); | 284 | try adapter.stream.readNoEof(buf[0..size]); |
| 285 | return buf; | 285 | return buf; |
| 286 | } | 286 | } |
| ... | @@ -592,7 +592,7 @@ pub const BufferedAtomicFile = struct { | ... | @@ -592,7 +592,7 @@ pub const BufferedAtomicFile = struct { |
| 592 | self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode); | 592 | self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode); |
| 593 | errdefer self.atomic_file.deinit(); | 593 | errdefer self.atomic_file.deinit(); |
| 594 | 594 | ||
| 595 | self.file_stream = FileOutStream.init(&self.atomic_file.file); | 595 | self.file_stream = FileOutStream.init(self.atomic_file.file); |
| 596 | self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream); | 596 | self.buffered_stream = BufferedOutStream(FileOutStream.Error).init(&self.file_stream.stream); |
| 597 | return self; | 597 | return self; |
| 598 | } | 598 | } |
| ... | @@ -622,7 +622,7 @@ test "import io tests" { | ... | @@ -622,7 +622,7 @@ test "import io tests" { |
| 622 | 622 | ||
| 623 | pub fn readLine(buf: []u8) !usize { | 623 | pub fn readLine(buf: []u8) !usize { |
| 624 | var stdin = getStdIn() catch return error.StdInUnavailable; | 624 | var stdin = getStdIn() catch return error.StdInUnavailable; |
| 625 | var adapter = FileInStream.init(&stdin); | 625 | var adapter = FileInStream.init(stdin); |
| 626 | var stream = &adapter.stream; | 626 | var stream = &adapter.stream; |
| 627 | var index: usize = 0; | 627 | var index: usize = 0; |
| 628 | while (true) { | 628 | while (true) { |
std/io_test.zig+2-2| ... | @@ -19,7 +19,7 @@ test "write a file, read it, then delete it" { | ... | @@ -19,7 +19,7 @@ test "write a file, read it, then delete it" { |
| 19 | var file = try os.File.openWrite(tmp_file_name); | 19 | var file = try os.File.openWrite(tmp_file_name); |
| 20 | defer file.close(); | 20 | defer file.close(); |
| 21 | 21 | ||
| 22 | var file_out_stream = io.FileOutStream.init(&file); | 22 | var file_out_stream = io.FileOutStream.init(file); |
| 23 | var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream); | 23 | var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream); |
| 24 | const st = &buf_stream.stream; | 24 | const st = &buf_stream.stream; |
| 25 | try st.print("begin"); | 25 | try st.print("begin"); |
| ... | @@ -35,7 +35,7 @@ test "write a file, read it, then delete it" { | ... | @@ -35,7 +35,7 @@ test "write a file, read it, then delete it" { |
| 35 | const expected_file_size = "begin".len + data.len + "end".len; | 35 | const expected_file_size = "begin".len + data.len + "end".len; |
| 36 | assert(file_size == expected_file_size); | 36 | assert(file_size == expected_file_size); |
| 37 | 37 | ||
| 38 | var file_in_stream = io.FileInStream.init(&file); | 38 | var file_in_stream = io.FileInStream.init(file); |
| 39 | var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream); | 39 | var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream); |
| 40 | const st = &buf_stream.stream; | 40 | const st = &buf_stream.stream; |
| 41 | const contents = try st.readAllAlloc(allocator, 2 * 1024); | 41 | const contents = try st.readAllAlloc(allocator, 2 * 1024); |
std/macho.zig+552-206| ... | @@ -1,4 +1,3 @@ | ... | @@ -1,4 +1,3 @@ |
| 1 | |||
| 2 | pub const mach_header = extern struct { | 1 | pub const mach_header = extern struct { |
| 3 | magic: u32, | 2 | magic: u32, |
| 4 | cputype: cpu_type_t, | 3 | cputype: cpu_type_t, |
| ... | @@ -25,26 +24,43 @@ pub const load_command = extern struct { | ... | @@ -25,26 +24,43 @@ pub const load_command = extern struct { |
| 25 | cmdsize: u32, | 24 | cmdsize: u32, |
| 26 | }; | 25 | }; |
| 27 | 26 | ||
| 28 | |||
| 29 | /// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD | 27 | /// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD |
| 30 | /// "stab" style symbol table information as described in the header files | 28 | /// "stab" style symbol table information as described in the header files |
| 31 | /// <nlist.h> and <stab.h>. | 29 | /// <nlist.h> and <stab.h>. |
| 32 | pub const symtab_command = extern struct { | 30 | pub const symtab_command = extern struct { |
| 33 | cmd: u32, /// LC_SYMTAB | 31 | /// LC_SYMTAB |
| 34 | cmdsize: u32, /// sizeof(struct symtab_command) | 32 | cmd: u32, |
| 35 | symoff: u32, /// symbol table offset | 33 | |
| 36 | nsyms: u32, /// number of symbol table entries | 34 | /// sizeof(struct symtab_command) |
| 37 | stroff: u32, /// string table offset | 35 | cmdsize: u32, |
| 38 | strsize: u32, /// string table size in bytes | 36 | |
| 37 | /// symbol table offset | ||
| 38 | symoff: u32, | ||
| 39 | |||
| 40 | /// number of symbol table entries | ||
| 41 | nsyms: u32, | ||
| 42 | |||
| 43 | /// string table offset | ||
| 44 | stroff: u32, | ||
| 45 | |||
| 46 | /// string table size in bytes | ||
| 47 | strsize: u32, | ||
| 39 | }; | 48 | }; |
| 40 | 49 | ||
| 41 | /// The linkedit_data_command contains the offsets and sizes of a blob | 50 | /// The linkedit_data_command contains the offsets and sizes of a blob |
| 42 | /// of data in the __LINKEDIT segment. | 51 | /// of data in the __LINKEDIT segment. |
| 43 | const linkedit_data_command = extern struct { | 52 | const linkedit_data_command = extern struct { |
| 44 | cmd: u32,/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT. | 53 | /// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT. |
| 45 | cmdsize: u32, /// sizeof(struct linkedit_data_command) | 54 | cmd: u32, |
| 46 | dataoff: u32 , /// file offset of data in __LINKEDIT segment | 55 | |
| 47 | datasize: u32 , /// file size of data in __LINKEDIT segment | 56 | /// sizeof(struct linkedit_data_command) |
| 57 | cmdsize: u32, | ||
| 58 | |||
| 59 | /// file offset of data in __LINKEDIT segment | ||
| 60 | dataoff: u32, | ||
| 61 | |||
| 62 | /// file size of data in __LINKEDIT segment | ||
| 63 | datasize: u32, | ||
| 48 | }; | 64 | }; |
| 49 | 65 | ||
| 50 | /// The segment load command indicates that a part of this file is to be | 66 | /// The segment load command indicates that a part of this file is to be |
| ... | @@ -58,16 +74,35 @@ const linkedit_data_command = extern struct { | ... | @@ -58,16 +74,35 @@ const linkedit_data_command = extern struct { |
| 58 | /// section structures directly follow the segment command and their size is | 74 | /// section structures directly follow the segment command and their size is |
| 59 | /// reflected in cmdsize. | 75 | /// reflected in cmdsize. |
| 60 | pub const segment_command = extern struct { | 76 | pub const segment_command = extern struct { |
| 61 | cmd: u32,/// LC_SEGMENT | 77 | /// LC_SEGMENT |
| 62 | cmdsize: u32,/// includes sizeof section structs | 78 | cmd: u32, |
| 63 | segname: [16]u8,/// segment name | 79 | |
| 64 | vmaddr: u32,/// memory address of this segment | 80 | /// includes sizeof section structs |
| 65 | vmsize: u32,/// memory size of this segment | 81 | cmdsize: u32, |
| 66 | fileoff: u32,/// file offset of this segment | 82 | |
| 67 | filesize: u32,/// amount to map from the file | 83 | /// segment name |
| 68 | maxprot: vm_prot_t,/// maximum VM protection | 84 | segname: [16]u8, |
| 69 | initprot: vm_prot_t,/// initial VM protection | 85 | |
| 70 | nsects: u32,/// number of sections in segment | 86 | /// memory address of this segment |
| 87 | vmaddr: u32, | ||
| 88 | |||
| 89 | /// memory size of this segment | ||
| 90 | vmsize: u32, | ||
| 91 | |||
| 92 | /// file offset of this segment | ||
| 93 | fileoff: u32, | ||
| 94 | |||
| 95 | /// amount to map from the file | ||
| 96 | filesize: u32, | ||
| 97 | |||
| 98 | /// maximum VM protection | ||
| 99 | maxprot: vm_prot_t, | ||
| 100 | |||
| 101 | /// initial VM protection | ||
| 102 | initprot: vm_prot_t, | ||
| 103 | |||
| 104 | /// number of sections in segment | ||
| 105 | nsects: u32, | ||
| 71 | flags: u32, | 106 | flags: u32, |
| 72 | }; | 107 | }; |
| 73 | 108 | ||
| ... | @@ -76,17 +111,36 @@ pub const segment_command = extern struct { | ... | @@ -76,17 +111,36 @@ pub const segment_command = extern struct { |
| 76 | /// sections then section_64 structures directly follow the 64-bit segment | 111 | /// sections then section_64 structures directly follow the 64-bit segment |
| 77 | /// command and their size is reflected in cmdsize. | 112 | /// command and their size is reflected in cmdsize. |
| 78 | pub const segment_command_64 = extern struct { | 113 | pub const segment_command_64 = extern struct { |
| 79 | cmd: u32, /// LC_SEGMENT_64 | 114 | /// LC_SEGMENT_64 |
| 80 | cmdsize: u32, /// includes sizeof section_64 structs | 115 | cmd: u32, |
| 81 | segname: [16]u8, /// segment name | 116 | |
| 82 | vmaddr: u64, /// memory address of this segment | 117 | /// includes sizeof section_64 structs |
| 83 | vmsize: u64, /// memory size of this segment | 118 | cmdsize: u32, |
| 84 | fileoff: u64, /// file offset of this segment | 119 | |
| 85 | filesize: u64, /// amount to map from the file | 120 | /// segment name |
| 86 | maxprot: vm_prot_t, /// maximum VM protection | 121 | segname: [16]u8, |
| 87 | initprot: vm_prot_t, /// initial VM protection | 122 | |
| 88 | nsects: u32, /// number of sections in segment | 123 | /// memory address of this segment |
| 89 | flags: u32, | 124 | vmaddr: u64, |
| 125 | |||
| 126 | /// memory size of this segment | ||
| 127 | vmsize: u64, | ||
| 128 | |||
| 129 | /// file offset of this segment | ||
| 130 | fileoff: u64, | ||
| 131 | |||
| 132 | /// amount to map from the file | ||
| 133 | filesize: u64, | ||
| 134 | |||
| 135 | /// maximum VM protection | ||
| 136 | maxprot: vm_prot_t, | ||
| 137 | |||
| 138 | /// initial VM protection | ||
| 139 | initprot: vm_prot_t, | ||
| 140 | |||
| 141 | /// number of sections in segment | ||
| 142 | nsects: u32, | ||
| 143 | flags: u32, | ||
| 90 | }; | 144 | }; |
| 91 | 145 | ||
| 92 | /// A segment is made up of zero or more sections. Non-MH_OBJECT files have | 146 | /// A segment is made up of zero or more sections. Non-MH_OBJECT files have |
| ... | @@ -115,32 +169,76 @@ pub const segment_command_64 = extern struct { | ... | @@ -115,32 +169,76 @@ pub const segment_command_64 = extern struct { |
| 115 | /// fields of the section structure for mach object files is described in the | 169 | /// fields of the section structure for mach object files is described in the |
| 116 | /// header file <reloc.h>. | 170 | /// header file <reloc.h>. |
| 117 | pub const @"section" = extern struct { | 171 | pub const @"section" = extern struct { |
| 118 | sectname: [16]u8, /// name of this section | 172 | /// name of this section |
| 119 | segname: [16]u8, /// segment this section goes in | 173 | sectname: [16]u8, |
| 120 | addr: u32, /// memory address of this section | 174 | |
| 121 | size: u32, /// size in bytes of this section | 175 | /// segment this section goes in |
| 122 | offset: u32, /// file offset of this section | 176 | segname: [16]u8, |
| 123 | @"align": u32, /// section alignment (power of 2) | 177 | |
| 124 | reloff: u32, /// file offset of relocation entries | 178 | /// memory address of this section |
| 125 | nreloc: u32, /// number of relocation entries | 179 | addr: u32, |
| 126 | flags: u32, /// flags (section type and attributes | 180 | |
| 127 | reserved1: u32, /// reserved (for offset or index) | 181 | /// size in bytes of this section |
| 128 | reserved2: u32, /// reserved (for count or sizeof) | 182 | size: u32, |
| 183 | |||
| 184 | /// file offset of this section | ||
| 185 | offset: u32, | ||
| 186 | |||
| 187 | /// section alignment (power of 2) | ||
| 188 | @"align": u32, | ||
| 189 | |||
| 190 | /// file offset of relocation entries | ||
| 191 | reloff: u32, | ||
| 192 | |||
| 193 | /// number of relocation entries | ||
| 194 | nreloc: u32, | ||
| 195 | |||
| 196 | /// flags (section type and attributes | ||
| 197 | flags: u32, | ||
| 198 | |||
| 199 | /// reserved (for offset or index) | ||
| 200 | reserved1: u32, | ||
| 201 | |||
| 202 | /// reserved (for count or sizeof) | ||
| 203 | reserved2: u32, | ||
| 129 | }; | 204 | }; |
| 130 | 205 | ||
| 131 | pub const section_64 = extern struct { | 206 | pub const section_64 = extern struct { |
| 132 | sectname: [16]u8, /// name of this section | 207 | /// name of this section |
| 133 | segname: [16]u8, /// segment this section goes in | 208 | sectname: [16]u8, |
| 134 | addr: u64, /// memory address of this section | 209 | |
| 135 | size: u64, /// size in bytes of this section | 210 | /// segment this section goes in |
| 136 | offset: u32, /// file offset of this section | 211 | segname: [16]u8, |
| 137 | @"align": u32, /// section alignment (power of 2) | 212 | |
| 138 | reloff: u32, /// file offset of relocation entries | 213 | /// memory address of this section |
| 139 | nreloc: u32, /// number of relocation entries | 214 | addr: u64, |
| 140 | flags: u32, /// flags (section type and attributes | 215 | |
| 141 | reserved1: u32, /// reserved (for offset or index) | 216 | /// size in bytes of this section |
| 142 | reserved2: u32, /// reserved (for count or sizeof) | 217 | size: u64, |
| 143 | reserved3: u32, /// reserved | 218 | |
| 219 | /// file offset of this section | ||
| 220 | offset: u32, | ||
| 221 | |||
| 222 | /// section alignment (power of 2) | ||
| 223 | @"align": u32, | ||
| 224 | |||
| 225 | /// file offset of relocation entries | ||
| 226 | reloff: u32, | ||
| 227 | |||
| 228 | /// number of relocation entries | ||
| 229 | nreloc: u32, | ||
| 230 | |||
| 231 | /// flags (section type and attributes | ||
| 232 | flags: u32, | ||
| 233 | |||
| 234 | /// reserved (for offset or index) | ||
| 235 | reserved1: u32, | ||
| 236 | |||
| 237 | /// reserved (for count or sizeof) | ||
| 238 | reserved2: u32, | ||
| 239 | |||
| 240 | /// reserved | ||
| 241 | reserved3: u32, | ||
| 144 | }; | 242 | }; |
| 145 | 243 | ||
| 146 | pub const nlist = extern struct { | 244 | pub const nlist = extern struct { |
| ... | @@ -168,116 +266,287 @@ pub const nlist_64 = extern struct { | ... | @@ -168,116 +266,287 @@ pub const nlist_64 = extern struct { |
| 168 | /// simply be ignored. | 266 | /// simply be ignored. |
| 169 | pub const LC_REQ_DYLD = 0x80000000; | 267 | pub const LC_REQ_DYLD = 0x80000000; |
| 170 | 268 | ||
| 171 | pub const LC_SEGMENT = 0x1; /// segment of this file to be mapped | 269 | /// segment of this file to be mapped |
| 172 | pub const LC_SYMTAB = 0x2; /// link-edit stab symbol table info | 270 | pub const LC_SEGMENT = 0x1; |
| 173 | pub const LC_SYMSEG = 0x3; /// link-edit gdb symbol table info (obsolete) | 271 | |
| 174 | pub const LC_THREAD = 0x4; /// thread | 272 | /// link-edit stab symbol table info |
| 175 | pub const LC_UNIXTHREAD = 0x5; /// unix thread (includes a stack) | 273 | pub const LC_SYMTAB = 0x2; |
| 176 | pub const LC_LOADFVMLIB = 0x6; /// load a specified fixed VM shared library | 274 | |
| 177 | pub const LC_IDFVMLIB = 0x7; /// fixed VM shared library identification | 275 | /// link-edit gdb symbol table info (obsolete) |
| 178 | pub const LC_IDENT = 0x8; /// object identification info (obsolete) | 276 | pub const LC_SYMSEG = 0x3; |
| 179 | pub const LC_FVMFILE = 0x9; /// fixed VM file inclusion (internal use) | 277 | |
| 180 | pub const LC_PREPAGE = 0xa; /// prepage command (internal use) | 278 | /// thread |
| 181 | pub const LC_DYSYMTAB = 0xb; /// dynamic link-edit symbol table info | 279 | pub const LC_THREAD = 0x4; |
| 182 | pub const LC_LOAD_DYLIB = 0xc; /// load a dynamically linked shared library | 280 | |
| 183 | pub const LC_ID_DYLIB = 0xd; /// dynamically linked shared lib ident | 281 | /// unix thread (includes a stack) |
| 184 | pub const LC_LOAD_DYLINKER = 0xe; /// load a dynamic linker | 282 | pub const LC_UNIXTHREAD = 0x5; |
| 185 | pub const LC_ID_DYLINKER = 0xf; /// dynamic linker identification | 283 | |
| 186 | pub const LC_PREBOUND_DYLIB = 0x10; /// modules prebound for a dynamically | 284 | /// load a specified fixed VM shared library |
| 187 | pub const LC_ROUTINES = 0x11; /// image routines | 285 | pub const LC_LOADFVMLIB = 0x6; |
| 188 | pub const LC_SUB_FRAMEWORK = 0x12; /// sub framework | 286 | |
| 189 | pub const LC_SUB_UMBRELLA = 0x13; /// sub umbrella | 287 | /// fixed VM shared library identification |
| 190 | pub const LC_SUB_CLIENT = 0x14; /// sub client | 288 | pub const LC_IDFVMLIB = 0x7; |
| 191 | pub const LC_SUB_LIBRARY = 0x15; /// sub library | 289 | |
| 192 | pub const LC_TWOLEVEL_HINTS = 0x16; /// two-level namespace lookup hints | 290 | /// object identification info (obsolete) |
| 193 | pub const LC_PREBIND_CKSUM = 0x17; /// prebind checksum | 291 | pub const LC_IDENT = 0x8; |
| 292 | |||
| 293 | /// fixed VM file inclusion (internal use) | ||
| 294 | pub const LC_FVMFILE = 0x9; | ||
| 295 | |||
| 296 | /// prepage command (internal use) | ||
| 297 | pub const LC_PREPAGE = 0xa; | ||
| 298 | |||
| 299 | /// dynamic link-edit symbol table info | ||
| 300 | pub const LC_DYSYMTAB = 0xb; | ||
| 301 | |||
| 302 | /// load a dynamically linked shared library | ||
| 303 | pub const LC_LOAD_DYLIB = 0xc; | ||
| 304 | |||
| 305 | /// dynamically linked shared lib ident | ||
| 306 | pub const LC_ID_DYLIB = 0xd; | ||
| 307 | |||
| 308 | /// load a dynamic linker | ||
| 309 | pub const LC_LOAD_DYLINKER = 0xe; | ||
| 310 | |||
| 311 | /// dynamic linker identification | ||
| 312 | pub const LC_ID_DYLINKER = 0xf; | ||
| 313 | |||
| 314 | /// modules prebound for a dynamically | ||
| 315 | pub const LC_PREBOUND_DYLIB = 0x10; | ||
| 316 | |||
| 317 | /// image routines | ||
| 318 | pub const LC_ROUTINES = 0x11; | ||
| 319 | |||
| 320 | /// sub framework | ||
| 321 | pub const LC_SUB_FRAMEWORK = 0x12; | ||
| 322 | |||
| 323 | /// sub umbrella | ||
| 324 | pub const LC_SUB_UMBRELLA = 0x13; | ||
| 325 | |||
| 326 | /// sub client | ||
| 327 | pub const LC_SUB_CLIENT = 0x14; | ||
| 328 | |||
| 329 | /// sub library | ||
| 330 | pub const LC_SUB_LIBRARY = 0x15; | ||
| 331 | |||
| 332 | /// two-level namespace lookup hints | ||
| 333 | pub const LC_TWOLEVEL_HINTS = 0x16; | ||
| 334 | |||
| 335 | /// prebind checksum | ||
| 336 | pub const LC_PREBIND_CKSUM = 0x17; | ||
| 194 | 337 | ||
| 195 | /// load a dynamically linked shared library that is allowed to be missing | 338 | /// load a dynamically linked shared library that is allowed to be missing |
| 196 | /// (all symbols are weak imported). | 339 | /// (all symbols are weak imported). |
| 197 | pub const LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD); | 340 | pub const LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD); |
| 198 | 341 | ||
| 199 | pub const LC_SEGMENT_64 = 0x19; /// 64-bit segment of this file to be mapped | 342 | /// 64-bit segment of this file to be mapped |
| 200 | pub const LC_ROUTINES_64 = 0x1a; /// 64-bit image routines | 343 | pub const LC_SEGMENT_64 = 0x19; |
| 201 | pub const LC_UUID = 0x1b; /// the uuid | 344 | |
| 202 | pub const LC_RPATH = (0x1c | LC_REQ_DYLD); /// runpath additions | 345 | /// 64-bit image routines |
| 203 | pub const LC_CODE_SIGNATURE = 0x1d; /// local of code signature | 346 | pub const LC_ROUTINES_64 = 0x1a; |
| 204 | pub const LC_SEGMENT_SPLIT_INFO = 0x1e; /// local of info to split segments | 347 | |
| 205 | pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD); /// load and re-export dylib | 348 | /// the uuid |
| 206 | pub const LC_LAZY_LOAD_DYLIB = 0x20; /// delay load of dylib until first use | 349 | pub const LC_UUID = 0x1b; |
| 207 | pub const LC_ENCRYPTION_INFO = 0x21; /// encrypted segment information | 350 | |
| 208 | pub const LC_DYLD_INFO = 0x22; /// compressed dyld information | 351 | /// runpath additions |
| 209 | pub const LC_DYLD_INFO_ONLY = (0x22|LC_REQ_DYLD); /// compressed dyld information only | 352 | pub const LC_RPATH = (0x1c | LC_REQ_DYLD); |
| 210 | pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD); /// load upward dylib | 353 | |
| 211 | pub const LC_VERSION_MIN_MACOSX = 0x24; /// build for MacOSX min OS version | 354 | /// local of code signature |
| 212 | pub const LC_VERSION_MIN_IPHONEOS = 0x25; /// build for iPhoneOS min OS version | 355 | pub const LC_CODE_SIGNATURE = 0x1d; |
| 213 | pub const LC_FUNCTION_STARTS = 0x26; /// compressed table of function start addresses | 356 | |
| 214 | pub const LC_DYLD_ENVIRONMENT = 0x27; /// string for dyld to treat like environment variable | 357 | /// local of info to split segments |
| 215 | pub const LC_MAIN = (0x28|LC_REQ_DYLD); /// replacement for LC_UNIXTHREAD | 358 | pub const LC_SEGMENT_SPLIT_INFO = 0x1e; |
| 216 | pub const LC_DATA_IN_CODE = 0x29; /// table of non-instructions in __text | 359 | |
| 217 | pub const LC_SOURCE_VERSION = 0x2A; /// source version used to build binary | 360 | /// load and re-export dylib |
| 218 | pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B; /// Code signing DRs copied from linked dylibs | 361 | pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD); |
| 219 | pub const LC_ENCRYPTION_INFO_64 = 0x2C; /// 64-bit encrypted segment information | 362 | |
| 220 | pub const LC_LINKER_OPTION = 0x2D; /// linker options in MH_OBJECT files | 363 | /// delay load of dylib until first use |
| 221 | pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E; /// optimization hints in MH_OBJECT files | 364 | pub const LC_LAZY_LOAD_DYLIB = 0x20; |
| 222 | pub const LC_VERSION_MIN_TVOS = 0x2F; /// build for AppleTV min OS version | 365 | |
| 223 | pub const LC_VERSION_MIN_WATCHOS = 0x30; /// build for Watch min OS version | 366 | /// encrypted segment information |
| 224 | pub const LC_NOTE = 0x31; /// arbitrary data included within a Mach-O file | 367 | pub const LC_ENCRYPTION_INFO = 0x21; |
| 225 | pub const LC_BUILD_VERSION = 0x32; /// build for platform min OS version | 368 | |
| 226 | 369 | /// compressed dyld information | |
| 227 | pub const MH_MAGIC = 0xfeedface; /// the mach magic number | 370 | pub const LC_DYLD_INFO = 0x22; |
| 228 | pub const MH_CIGAM = 0xcefaedfe; /// NXSwapInt(MH_MAGIC) | 371 | |
| 229 | 372 | /// compressed dyld information only | |
| 230 | pub const MH_MAGIC_64 = 0xfeedfacf; /// the 64-bit mach magic number | 373 | pub const LC_DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD); |
| 231 | pub const MH_CIGAM_64 = 0xcffaedfe; /// NXSwapInt(MH_MAGIC_64) | 374 | |
| 232 | 375 | /// load upward dylib | |
| 233 | pub const MH_OBJECT = 0x1; /// relocatable object file | 376 | pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD); |
| 234 | pub const MH_EXECUTE = 0x2; /// demand paged executable file | 377 | |
| 235 | pub const MH_FVMLIB = 0x3; /// fixed VM shared library file | 378 | /// build for MacOSX min OS version |
| 236 | pub const MH_CORE = 0x4; /// core file | 379 | pub const LC_VERSION_MIN_MACOSX = 0x24; |
| 237 | pub const MH_PRELOAD = 0x5; /// preloaded executable file | 380 | |
| 238 | pub const MH_DYLIB = 0x6; /// dynamically bound shared library | 381 | /// build for iPhoneOS min OS version |
| 239 | pub const MH_DYLINKER = 0x7; /// dynamic link editor | 382 | pub const LC_VERSION_MIN_IPHONEOS = 0x25; |
| 240 | pub const MH_BUNDLE = 0x8; /// dynamically bound bundle file | 383 | |
| 241 | pub const MH_DYLIB_STUB = 0x9; /// shared library stub for static linking only, no section contents | 384 | /// compressed table of function start addresses |
| 242 | pub const MH_DSYM = 0xa; /// companion file with only debug sections | 385 | pub const LC_FUNCTION_STARTS = 0x26; |
| 243 | pub const MH_KEXT_BUNDLE = 0xb; /// x86_64 kexts | 386 | |
| 387 | /// string for dyld to treat like environment variable | ||
| 388 | pub const LC_DYLD_ENVIRONMENT = 0x27; | ||
| 389 | |||
| 390 | /// replacement for LC_UNIXTHREAD | ||
| 391 | pub const LC_MAIN = (0x28 | LC_REQ_DYLD); | ||
| 392 | |||
| 393 | /// table of non-instructions in __text | ||
| 394 | pub const LC_DATA_IN_CODE = 0x29; | ||
| 395 | |||
| 396 | /// source version used to build binary | ||
| 397 | pub const LC_SOURCE_VERSION = 0x2A; | ||
| 398 | |||
| 399 | /// Code signing DRs copied from linked dylibs | ||
| 400 | pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B; | ||
| 401 | |||
| 402 | /// 64-bit encrypted segment information | ||
| 403 | pub const LC_ENCRYPTION_INFO_64 = 0x2C; | ||
| 404 | |||
| 405 | /// linker options in MH_OBJECT files | ||
| 406 | pub const LC_LINKER_OPTION = 0x2D; | ||
| 407 | |||
| 408 | /// optimization hints in MH_OBJECT files | ||
| 409 | pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E; | ||
| 410 | |||
| 411 | /// build for AppleTV min OS version | ||
| 412 | pub const LC_VERSION_MIN_TVOS = 0x2F; | ||
| 413 | |||
| 414 | /// build for Watch min OS version | ||
| 415 | pub const LC_VERSION_MIN_WATCHOS = 0x30; | ||
| 416 | |||
| 417 | /// arbitrary data included within a Mach-O file | ||
| 418 | pub const LC_NOTE = 0x31; | ||
| 419 | |||
| 420 | /// build for platform min OS version | ||
| 421 | pub const LC_BUILD_VERSION = 0x32; | ||
| 422 | |||
| 423 | /// the mach magic number | ||
| 424 | pub const MH_MAGIC = 0xfeedface; | ||
| 425 | |||
| 426 | /// NXSwapInt(MH_MAGIC) | ||
| 427 | pub const MH_CIGAM = 0xcefaedfe; | ||
| 428 | |||
| 429 | /// the 64-bit mach magic number | ||
| 430 | pub const MH_MAGIC_64 = 0xfeedfacf; | ||
| 431 | |||
| 432 | /// NXSwapInt(MH_MAGIC_64) | ||
| 433 | pub const MH_CIGAM_64 = 0xcffaedfe; | ||
| 434 | |||
| 435 | /// relocatable object file | ||
| 436 | pub const MH_OBJECT = 0x1; | ||
| 437 | |||
| 438 | /// demand paged executable file | ||
| 439 | pub const MH_EXECUTE = 0x2; | ||
| 440 | |||
| 441 | /// fixed VM shared library file | ||
| 442 | pub const MH_FVMLIB = 0x3; | ||
| 443 | |||
| 444 | /// core file | ||
| 445 | pub const MH_CORE = 0x4; | ||
| 446 | |||
| 447 | /// preloaded executable file | ||
| 448 | pub const MH_PRELOAD = 0x5; | ||
| 449 | |||
| 450 | /// dynamically bound shared library | ||
| 451 | pub const MH_DYLIB = 0x6; | ||
| 452 | |||
| 453 | /// dynamic link editor | ||
| 454 | pub const MH_DYLINKER = 0x7; | ||
| 455 | |||
| 456 | /// dynamically bound bundle file | ||
| 457 | pub const MH_BUNDLE = 0x8; | ||
| 458 | |||
| 459 | /// shared library stub for static linking only, no section contents | ||
| 460 | pub const MH_DYLIB_STUB = 0x9; | ||
| 461 | |||
| 462 | /// companion file with only debug sections | ||
| 463 | pub const MH_DSYM = 0xa; | ||
| 464 | |||
| 465 | /// x86_64 kexts | ||
| 466 | pub const MH_KEXT_BUNDLE = 0xb; | ||
| 244 | 467 | ||
| 245 | // Constants for the flags field of the mach_header | 468 | // Constants for the flags field of the mach_header |
| 246 | 469 | ||
| 247 | pub const MH_NOUNDEFS = 0x1; /// the object file has no undefined references | 470 | /// the object file has no undefined references |
| 248 | pub const MH_INCRLINK = 0x2; /// the object file is the output of an incremental link against a base file and can't be link edited again | 471 | pub const MH_NOUNDEFS = 0x1; |
| 249 | pub const MH_DYLDLINK = 0x4; /// the object file is input for the dynamic linker and can't be staticly link edited again | 472 | |
| 250 | pub const MH_BINDATLOAD = 0x8; /// the object file's undefined references are bound by the dynamic linker when loaded. | 473 | /// the object file is the output of an incremental link against a base file and can't be link edited again |
| 251 | pub const MH_PREBOUND = 0x10; /// the file has its dynamic undefined references prebound. | 474 | pub const MH_INCRLINK = 0x2; |
| 252 | pub const MH_SPLIT_SEGS = 0x20; /// the file has its read-only and read-write segments split | 475 | |
| 253 | pub const MH_LAZY_INIT = 0x40; /// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete) | 476 | /// the object file is input for the dynamic linker and can't be staticly link edited again |
| 254 | pub const MH_TWOLEVEL = 0x80; /// the image is using two-level name space bindings | 477 | pub const MH_DYLDLINK = 0x4; |
| 255 | pub const MH_FORCE_FLAT = 0x100; /// the executable is forcing all images to use flat name space bindings | 478 | |
| 256 | pub const MH_NOMULTIDEFS = 0x200; /// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used. | 479 | /// the object file's undefined references are bound by the dynamic linker when loaded. |
| 257 | pub const MH_NOFIXPREBINDING = 0x400; /// do not have dyld notify the prebinding agent about this executable | 480 | pub const MH_BINDATLOAD = 0x8; |
| 258 | pub const MH_PREBINDABLE = 0x800; /// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. | 481 | |
| 259 | pub const MH_ALLMODSBOUND = 0x1000; /// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. | 482 | /// the file has its dynamic undefined references prebound. |
| 260 | pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;/// safe to divide up the sections into sub-sections via symbols for dead code stripping | 483 | pub const MH_PREBOUND = 0x10; |
| 261 | pub const MH_CANONICAL = 0x4000; /// the binary has been canonicalized via the unprebind operation | 484 | |
| 262 | pub const MH_WEAK_DEFINES = 0x8000; /// the final linked image contains external weak symbols | 485 | /// the file has its read-only and read-write segments split |
| 263 | pub const MH_BINDS_TO_WEAK = 0x10000; /// the final linked image uses weak symbols | 486 | pub const MH_SPLIT_SEGS = 0x20; |
| 264 | 487 | ||
| 265 | pub const MH_ALLOW_STACK_EXECUTION = 0x20000;/// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. | 488 | /// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete) |
| 266 | pub const MH_ROOT_SAFE = 0x40000; /// When this bit is set, the binary declares it is safe for use in processes with uid zero | 489 | pub const MH_LAZY_INIT = 0x40; |
| 267 | 490 | ||
| 268 | pub const MH_SETUID_SAFE = 0x80000; /// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true | 491 | /// the image is using two-level name space bindings |
| 269 | 492 | pub const MH_TWOLEVEL = 0x80; | |
| 270 | pub const MH_NO_REEXPORTED_DYLIBS = 0x100000; /// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported | 493 | |
| 271 | pub const MH_PIE = 0x200000; /// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. | 494 | /// the executable is forcing all images to use flat name space bindings |
| 272 | pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000; /// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib. | 495 | pub const MH_FORCE_FLAT = 0x100; |
| 273 | pub const MH_HAS_TLV_DESCRIPTORS = 0x800000; /// Contains a section of type S_THREAD_LOCAL_VARIABLES | 496 | |
| 274 | 497 | /// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used. | |
| 275 | pub const MH_NO_HEAP_EXECUTION = 0x1000000; /// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes. | 498 | pub const MH_NOMULTIDEFS = 0x200; |
| 276 | 499 | ||
| 277 | pub const MH_APP_EXTENSION_SAFE = 0x02000000; /// The code was linked for use in an application extension. | 500 | /// do not have dyld notify the prebinding agent about this executable |
| 278 | 501 | pub const MH_NOFIXPREBINDING = 0x400; | |
| 279 | pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info. | 502 | |
| 503 | /// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. | ||
| 504 | pub const MH_PREBINDABLE = 0x800; | ||
| 505 | |||
| 506 | /// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. | ||
| 507 | pub const MH_ALLMODSBOUND = 0x1000; | ||
| 508 | |||
| 509 | /// safe to divide up the sections into sub-sections via symbols for dead code stripping | ||
| 510 | pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000; | ||
| 511 | |||
| 512 | /// the binary has been canonicalized via the unprebind operation | ||
| 513 | pub const MH_CANONICAL = 0x4000; | ||
| 514 | |||
| 515 | /// the final linked image contains external weak symbols | ||
| 516 | pub const MH_WEAK_DEFINES = 0x8000; | ||
| 517 | |||
| 518 | /// the final linked image uses weak symbols | ||
| 519 | pub const MH_BINDS_TO_WEAK = 0x10000; | ||
| 520 | |||
| 521 | /// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. | ||
| 522 | pub const MH_ALLOW_STACK_EXECUTION = 0x20000; | ||
| 523 | |||
| 524 | /// When this bit is set, the binary declares it is safe for use in processes with uid zero | ||
| 525 | pub const MH_ROOT_SAFE = 0x40000; | ||
| 526 | |||
| 527 | /// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true | ||
| 528 | pub const MH_SETUID_SAFE = 0x80000; | ||
| 529 | |||
| 530 | /// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported | ||
| 531 | pub const MH_NO_REEXPORTED_DYLIBS = 0x100000; | ||
| 280 | 532 | ||
| 533 | /// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. | ||
| 534 | pub const MH_PIE = 0x200000; | ||
| 535 | |||
| 536 | /// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib. | ||
| 537 | pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000; | ||
| 538 | |||
| 539 | /// Contains a section of type S_THREAD_LOCAL_VARIABLES | ||
| 540 | pub const MH_HAS_TLV_DESCRIPTORS = 0x800000; | ||
| 541 | |||
| 542 | /// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes. | ||
| 543 | pub const MH_NO_HEAP_EXECUTION = 0x1000000; | ||
| 544 | |||
| 545 | /// The code was linked for use in an application extension. | ||
| 546 | pub const MH_APP_EXTENSION_SAFE = 0x02000000; | ||
| 547 | |||
| 548 | /// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info. | ||
| 549 | pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; | ||
| 281 | 550 | ||
| 282 | /// The flags field of a section structure is separated into two parts a section | 551 | /// The flags field of a section structure is separated into two parts a section |
| 283 | /// type and section attributes. The section types are mutually exclusive (it | 552 | /// type and section attributes. The section types are mutually exclusive (it |
| ... | @@ -285,52 +554,129 @@ pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbol | ... | @@ -285,52 +554,129 @@ pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbol |
| 285 | /// than one attribute). | 554 | /// than one attribute). |
| 286 | /// 256 section types | 555 | /// 256 section types |
| 287 | pub const SECTION_TYPE = 0x000000ff; | 556 | pub const SECTION_TYPE = 0x000000ff; |
| 288 | pub const SECTION_ATTRIBUTES = 0xffffff00; /// 24 section attributes | 557 | |
| 289 | 558 | /// 24 section attributes | |
| 290 | pub const S_REGULAR = 0x0; /// regular section | 559 | pub const SECTION_ATTRIBUTES = 0xffffff00; |
| 291 | pub const S_ZEROFILL = 0x1; /// zero fill on demand section | 560 | |
| 292 | pub const S_CSTRING_LITERALS = 0x2; /// section with only literal C string | 561 | /// regular section |
| 293 | pub const S_4BYTE_LITERALS = 0x3; /// section with only 4 byte literals | 562 | pub const S_REGULAR = 0x0; |
| 294 | pub const S_8BYTE_LITERALS = 0x4; /// section with only 8 byte literals | 563 | |
| 295 | pub const S_LITERAL_POINTERS = 0x5; /// section with only pointers to | 564 | /// zero fill on demand section |
| 296 | 565 | pub const S_ZEROFILL = 0x1; | |
| 297 | 566 | ||
| 298 | pub const N_STAB = 0xe0; /// if any of these bits set, a symbolic debugging entry | 567 | /// section with only literal C string |
| 299 | pub const N_PEXT = 0x10; /// private external symbol bit | 568 | pub const S_CSTRING_LITERALS = 0x2; |
| 300 | pub const N_TYPE = 0x0e; /// mask for the type bits | 569 | |
| 301 | pub const N_EXT = 0x01; /// external symbol bit, set for external symbols | 570 | /// section with only 4 byte literals |
| 302 | 571 | pub const S_4BYTE_LITERALS = 0x3; | |
| 303 | 572 | ||
| 304 | pub const N_GSYM = 0x20; /// global symbol: name,,NO_SECT,type,0 | 573 | /// section with only 8 byte literals |
| 305 | pub const N_FNAME = 0x22; /// procedure name (f77 kludge): name,,NO_SECT,0,0 | 574 | pub const S_8BYTE_LITERALS = 0x4; |
| 306 | pub const N_FUN = 0x24; /// procedure: name,,n_sect,linenumber,address | 575 | |
| 307 | pub const N_STSYM = 0x26; /// static symbol: name,,n_sect,type,address | 576 | /// section with only pointers to |
| 308 | pub const N_LCSYM = 0x28; /// .lcomm symbol: name,,n_sect,type,address | 577 | pub const S_LITERAL_POINTERS = 0x5; |
| 309 | pub const N_BNSYM = 0x2e; /// begin nsect sym: 0,,n_sect,0,address | 578 | |
| 310 | pub const N_AST = 0x32; /// AST file path: name,,NO_SECT,0,0 | 579 | /// if any of these bits set, a symbolic debugging entry |
| 311 | pub const N_OPT = 0x3c; /// emitted with gcc2_compiled and in gcc source | 580 | pub const N_STAB = 0xe0; |
| 312 | pub const N_RSYM = 0x40; /// register sym: name,,NO_SECT,type,register | 581 | |
| 313 | pub const N_SLINE = 0x44; /// src line: 0,,n_sect,linenumber,address | 582 | /// private external symbol bit |
| 314 | pub const N_ENSYM = 0x4e; /// end nsect sym: 0,,n_sect,0,address | 583 | pub const N_PEXT = 0x10; |
| 315 | pub const N_SSYM = 0x60; /// structure elt: name,,NO_SECT,type,struct_offset | 584 | |
| 316 | pub const N_SO = 0x64; /// source file name: name,,n_sect,0,address | 585 | /// mask for the type bits |
| 317 | pub const N_OSO = 0x66; /// object file name: name,,0,0,st_mtime | 586 | pub const N_TYPE = 0x0e; |
| 318 | pub const N_LSYM = 0x80; /// local sym: name,,NO_SECT,type,offset | 587 | |
| 319 | pub const N_BINCL = 0x82; /// include file beginning: name,,NO_SECT,0,sum | 588 | /// external symbol bit, set for external symbols |
| 320 | pub const N_SOL = 0x84; /// #included file name: name,,n_sect,0,address | 589 | pub const N_EXT = 0x01; |
| 321 | pub const N_PARAMS = 0x86; /// compiler parameters: name,,NO_SECT,0,0 | 590 | |
| 322 | pub const N_VERSION = 0x88; /// compiler version: name,,NO_SECT,0,0 | 591 | /// global symbol: name,,NO_SECT,type,0 |
| 323 | pub const N_OLEVEL = 0x8A; /// compiler -O level: name,,NO_SECT,0,0 | 592 | pub const N_GSYM = 0x20; |
| 324 | pub const N_PSYM = 0xa0; /// parameter: name,,NO_SECT,type,offset | 593 | |
| 325 | pub const N_EINCL = 0xa2; /// include file end: name,,NO_SECT,0,0 | 594 | /// procedure name (f77 kludge): name,,NO_SECT,0,0 |
| 326 | pub const N_ENTRY = 0xa4; /// alternate entry: name,,n_sect,linenumber,address | 595 | pub const N_FNAME = 0x22; |
| 327 | pub const N_LBRAC = 0xc0; /// left bracket: 0,,NO_SECT,nesting level,address | 596 | |
| 328 | pub const N_EXCL = 0xc2; /// deleted include file: name,,NO_SECT,0,sum | 597 | /// procedure: name,,n_sect,linenumber,address |
| 329 | pub const N_RBRAC = 0xe0; /// right bracket: 0,,NO_SECT,nesting level,address | 598 | pub const N_FUN = 0x24; |
| 330 | pub const N_BCOMM = 0xe2; /// begin common: name,,NO_SECT,0,0 | 599 | |
| 331 | pub const N_ECOMM = 0xe4; /// end common: name,,n_sect,0,0 | 600 | /// static symbol: name,,n_sect,type,address |
| 332 | pub const N_ECOML = 0xe8; /// end common (local name): 0,,n_sect,0,address | 601 | pub const N_STSYM = 0x26; |
| 333 | pub const N_LENG = 0xfe; /// second stab entry with length information | 602 | |
| 603 | /// .lcomm symbol: name,,n_sect,type,address | ||
| 604 | pub const N_LCSYM = 0x28; | ||
| 605 | |||
| 606 | /// begin nsect sym: 0,,n_sect,0,address | ||
| 607 | pub const N_BNSYM = 0x2e; | ||
| 608 | |||
| 609 | /// AST file path: name,,NO_SECT,0,0 | ||
| 610 | pub const N_AST = 0x32; | ||
| 611 | |||
| 612 | /// emitted with gcc2_compiled and in gcc source | ||
| 613 | pub const N_OPT = 0x3c; | ||
| 614 | |||
| 615 | /// register sym: name,,NO_SECT,type,register | ||
| 616 | pub const N_RSYM = 0x40; | ||
| 617 | |||
| 618 | /// src line: 0,,n_sect,linenumber,address | ||
| 619 | pub const N_SLINE = 0x44; | ||
| 620 | |||
| 621 | /// end nsect sym: 0,,n_sect,0,address | ||
| 622 | pub const N_ENSYM = 0x4e; | ||
| 623 | |||
| 624 | /// structure elt: name,,NO_SECT,type,struct_offset | ||
| 625 | pub const N_SSYM = 0x60; | ||
| 626 | |||
| 627 | /// source file name: name,,n_sect,0,address | ||
| 628 | pub const N_SO = 0x64; | ||
| 629 | |||
| 630 | /// object file name: name,,0,0,st_mtime | ||
| 631 | pub const N_OSO = 0x66; | ||
| 632 | |||
| 633 | /// local sym: name,,NO_SECT,type,offset | ||
| 634 | pub const N_LSYM = 0x80; | ||
| 635 | |||
| 636 | /// include file beginning: name,,NO_SECT,0,sum | ||
| 637 | pub const N_BINCL = 0x82; | ||
| 638 | |||
| 639 | /// #included file name: name,,n_sect,0,address | ||
| 640 | pub const N_SOL = 0x84; | ||
| 641 | |||
| 642 | /// compiler parameters: name,,NO_SECT,0,0 | ||
| 643 | pub const N_PARAMS = 0x86; | ||
| 644 | |||
| 645 | /// compiler version: name,,NO_SECT,0,0 | ||
| 646 | pub const N_VERSION = 0x88; | ||
| 647 | |||
| 648 | /// compiler -O level: name,,NO_SECT,0,0 | ||
| 649 | pub const N_OLEVEL = 0x8A; | ||
| 650 | |||
| 651 | /// parameter: name,,NO_SECT,type,offset | ||
| 652 | pub const N_PSYM = 0xa0; | ||
| 653 | |||
| 654 | /// include file end: name,,NO_SECT,0,0 | ||
| 655 | pub const N_EINCL = 0xa2; | ||
| 656 | |||
| 657 | /// alternate entry: name,,n_sect,linenumber,address | ||
| 658 | pub const N_ENTRY = 0xa4; | ||
| 659 | |||
| 660 | /// left bracket: 0,,NO_SECT,nesting level,address | ||
| 661 | pub const N_LBRAC = 0xc0; | ||
| 662 | |||
| 663 | /// deleted include file: name,,NO_SECT,0,sum | ||
| 664 | pub const N_EXCL = 0xc2; | ||
| 665 | |||
| 666 | /// right bracket: 0,,NO_SECT,nesting level,address | ||
| 667 | pub const N_RBRAC = 0xe0; | ||
| 668 | |||
| 669 | /// begin common: name,,NO_SECT,0,0 | ||
| 670 | pub const N_BCOMM = 0xe2; | ||
| 671 | |||
| 672 | /// end common: name,,n_sect,0,0 | ||
| 673 | pub const N_ECOMM = 0xe4; | ||
| 674 | |||
| 675 | /// end common (local name): 0,,n_sect,0,address | ||
| 676 | pub const N_ECOML = 0xe8; | ||
| 677 | |||
| 678 | /// second stab entry with length information | ||
| 679 | pub const N_LENG = 0xfe; | ||
| 334 | 680 | ||
| 335 | /// If a segment contains any sections marked with S_ATTR_DEBUG then all | 681 | /// If a segment contains any sections marked with S_ATTR_DEBUG then all |
| 336 | /// sections in that segment must have this attribute. No section other than | 682 | /// sections in that segment must have this attribute. No section other than |
| ... | @@ -339,10 +685,10 @@ pub const N_LENG = 0xfe; /// second stab entry with length information | ... | @@ -339,10 +685,10 @@ pub const N_LENG = 0xfe; /// second stab entry with length information |
| 339 | /// a section type S_REGULAR. The static linker will not copy section contents | 685 | /// a section type S_REGULAR. The static linker will not copy section contents |
| 340 | /// from sections with this attribute into its output file. These sections | 686 | /// from sections with this attribute into its output file. These sections |
| 341 | /// generally contain DWARF debugging info. | 687 | /// generally contain DWARF debugging info. |
| 342 | pub const S_ATTR_DEBUG = 0x02000000; /// a debug section | 688 | /// a debug section |
| 689 | pub const S_ATTR_DEBUG = 0x02000000; | ||
| 343 | 690 | ||
| 344 | pub const cpu_type_t = integer_t; | 691 | pub const cpu_type_t = integer_t; |
| 345 | pub const cpu_subtype_t = integer_t; | 692 | pub const cpu_subtype_t = integer_t; |
| 346 | pub const integer_t = c_int; | 693 | pub const integer_t = c_int; |
| 347 | pub const vm_prot_t = c_int; | 694 | pub const vm_prot_t = c_int; |
| 348 |
std/os/child_process.zig+48-14| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../index.zig"); |
| 2 | const cstr = std.cstr; | 2 | const cstr = std.cstr; |
| 3 | const unicode = std.unicode; | ||
| 3 | const io = std.io; | 4 | const io = std.io; |
| 4 | const os = std.os; | 5 | const os = std.os; |
| 5 | const posix = os.posix; | 6 | const posix = os.posix; |
| ... | @@ -12,6 +13,7 @@ const Buffer = std.Buffer; | ... | @@ -12,6 +13,7 @@ const Buffer = std.Buffer; |
| 12 | const builtin = @import("builtin"); | 13 | const builtin = @import("builtin"); |
| 13 | const Os = builtin.Os; | 14 | const Os = builtin.Os; |
| 14 | const LinkedList = std.LinkedList; | 15 | const LinkedList = std.LinkedList; |
| 16 | const windows_util = @import("windows/util.zig"); | ||
| 15 | 17 | ||
| 16 | const is_windows = builtin.os == Os.windows; | 18 | const is_windows = builtin.os == Os.windows; |
| 17 | 19 | ||
| ... | @@ -209,8 +211,8 @@ pub const ChildProcess = struct { | ... | @@ -209,8 +211,8 @@ pub const ChildProcess = struct { |
| 209 | defer Buffer.deinit(&stdout); | 211 | defer Buffer.deinit(&stdout); |
| 210 | defer Buffer.deinit(&stderr); | 212 | defer Buffer.deinit(&stderr); |
| 211 | 213 | ||
| 212 | var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); | 214 | var stdout_file_in_stream = io.FileInStream.init(child.stdout.?); |
| 213 | var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); | 215 | var stderr_file_in_stream = io.FileInStream.init(child.stderr.?); |
| 214 | 216 | ||
| 215 | try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); | 217 | try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); |
| 216 | try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size); | 218 | try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size); |
| ... | @@ -520,8 +522,8 @@ pub const ChildProcess = struct { | ... | @@ -520,8 +522,8 @@ pub const ChildProcess = struct { |
| 520 | const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv); | 522 | const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv); |
| 521 | defer self.allocator.free(cmd_line); | 523 | defer self.allocator.free(cmd_line); |
| 522 | 524 | ||
| 523 | var siStartInfo = windows.STARTUPINFOA{ | 525 | var siStartInfo = windows.STARTUPINFOW{ |
| 524 | .cb = @sizeOf(windows.STARTUPINFOA), | 526 | .cb = @sizeOf(windows.STARTUPINFOW), |
| 525 | .hStdError = g_hChildStd_ERR_Wr, | 527 | .hStdError = g_hChildStd_ERR_Wr, |
| 526 | .hStdOutput = g_hChildStd_OUT_Wr, | 528 | .hStdOutput = g_hChildStd_OUT_Wr, |
| 527 | .hStdInput = g_hChildStd_IN_Rd, | 529 | .hStdInput = g_hChildStd_IN_Rd, |
| ... | @@ -545,7 +547,9 @@ pub const ChildProcess = struct { | ... | @@ -545,7 +547,9 @@ pub const ChildProcess = struct { |
| 545 | 547 | ||
| 546 | const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null; | 548 | const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null; |
| 547 | defer if (cwd_slice) |cwd| self.allocator.free(cwd); | 549 | defer if (cwd_slice) |cwd| self.allocator.free(cwd); |
| 548 | const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null; | 550 | const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null; |
| 551 | defer if (cwd_w) |cwd| self.allocator.free(cwd); | ||
| 552 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; | ||
| 549 | 553 | ||
| 550 | const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null; | 554 | const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null; |
| 551 | defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); | 555 | defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); |
| ... | @@ -564,7 +568,13 @@ pub const ChildProcess = struct { | ... | @@ -564,7 +568,13 @@ pub const ChildProcess = struct { |
| 564 | }; | 568 | }; |
| 565 | defer self.allocator.free(app_name); | 569 | defer self.allocator.free(app_name); |
| 566 | 570 | ||
| 567 | windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { | 571 | const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name); |
| 572 | defer self.allocator.free(app_name_w); | ||
| 573 | |||
| 574 | const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line); | ||
| 575 | defer self.allocator.free(cmd_line_w); | ||
| 576 | |||
| 577 | windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { | ||
| 568 | if (no_path_err != error.FileNotFound) return no_path_err; | 578 | if (no_path_err != error.FileNotFound) return no_path_err; |
| 569 | 579 | ||
| 570 | const PATH = try os.getEnvVarOwned(self.allocator, "PATH"); | 580 | const PATH = try os.getEnvVarOwned(self.allocator, "PATH"); |
| ... | @@ -575,7 +585,10 @@ pub const ChildProcess = struct { | ... | @@ -575,7 +585,10 @@ pub const ChildProcess = struct { |
| 575 | const joined_path = try os.path.join(self.allocator, search_path, app_name); | 585 | const joined_path = try os.path.join(self.allocator, search_path, app_name); |
| 576 | defer self.allocator.free(joined_path); | 586 | defer self.allocator.free(joined_path); |
| 577 | 587 | ||
| 578 | if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| { | 588 | const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name); |
| 589 | defer self.allocator.free(joined_path_w); | ||
| 590 | |||
| 591 | if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| { | ||
| 579 | break; | 592 | break; |
| 580 | } else |err| if (err == error.FileNotFound) { | 593 | } else |err| if (err == error.FileNotFound) { |
| 581 | continue; | 594 | continue; |
| ... | @@ -626,15 +639,36 @@ pub const ChildProcess = struct { | ... | @@ -626,15 +639,36 @@ pub const ChildProcess = struct { |
| 626 | } | 639 | } |
| 627 | }; | 640 | }; |
| 628 | 641 | ||
| 629 | fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { | 642 | fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { |
| 630 | if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { | 643 | // TODO the docs for environment pointer say: |
| 644 | // > A pointer to the environment block for the new process. If this parameter | ||
| 645 | // > is NULL, the new process uses the environment of the calling process. | ||
| 646 | // > ... | ||
| 647 | // > An environment block can contain either Unicode or ANSI characters. If | ||
| 648 | // > the environment block pointed to by lpEnvironment contains Unicode | ||
| 649 | // > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT. | ||
| 650 | // > If this parameter is NULL and the environment block of the parent process | ||
| 651 | // > contains Unicode characters, you must also ensure that dwCreationFlags | ||
| 652 | // > includes CREATE_UNICODE_ENVIRONMENT. | ||
| 653 | // This seems to imply that we have to somehow know whether our process parent passed | ||
| 654 | // CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter. | ||
| 655 | // Since we do not know this information that would imply that we must not pass NULL | ||
| 656 | // for the parameter. | ||
| 657 | // However this would imply that programs compiled with -DUNICODE could not pass | ||
| 658 | // environment variables to programs that were not, which seems unlikely. | ||
| 659 | // More investigation is needed. | ||
| 660 | if (windows.CreateProcessW( | ||
| 661 | app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT, | ||
| 662 | @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation, | ||
| 663 | ) == 0) { | ||
| 631 | const err = windows.GetLastError(); | 664 | const err = windows.GetLastError(); |
| 632 | return switch (err) { | 665 | switch (err) { |
| 633 | windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, | 666 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, |
| 667 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 634 | windows.ERROR.INVALID_PARAMETER => unreachable, | 668 | windows.ERROR.INVALID_PARAMETER => unreachable, |
| 635 | windows.ERROR.INVALID_NAME => error.InvalidName, | 669 | windows.ERROR.INVALID_NAME => return error.InvalidName, |
| 636 | else => os.unexpectedErrorWindows(err), | 670 | else => return os.unexpectedErrorWindows(err), |
| 637 | }; | 671 | } |
| 638 | } | 672 | } |
| 639 | } | 673 | } |
| 640 | 674 |
std/os/file.zig+49-34| ... | @@ -48,18 +48,23 @@ pub const File = struct { | ... | @@ -48,18 +48,23 @@ pub const File = struct { |
| 48 | return openReadC(&path_c); | 48 | return openReadC(&path_c); |
| 49 | } | 49 | } |
| 50 | if (is_windows) { | 50 | if (is_windows) { |
| 51 | const handle = try os.windowsOpen( | 51 | const path_w = try windows_util.sliceToPrefixedFileW(path); |
| 52 | path, | 52 | return openReadW(&path_w); |
| 53 | windows.GENERIC_READ, | ||
| 54 | windows.FILE_SHARE_READ, | ||
| 55 | windows.OPEN_EXISTING, | ||
| 56 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 57 | ); | ||
| 58 | return openHandle(handle); | ||
| 59 | } | 53 | } |
| 60 | @compileError("Unsupported OS"); | 54 | @compileError("Unsupported OS"); |
| 61 | } | 55 | } |
| 62 | 56 | ||
| 57 | pub fn openReadW(path_w: [*]const u16) OpenError!File { | ||
| 58 | const handle = try os.windowsOpenW( | ||
| 59 | path_w, | ||
| 60 | windows.GENERIC_READ, | ||
| 61 | windows.FILE_SHARE_READ, | ||
| 62 | windows.OPEN_EXISTING, | ||
| 63 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 64 | ); | ||
| 65 | return openHandle(handle); | ||
| 66 | } | ||
| 67 | |||
| 63 | /// Calls `openWriteMode` with os.File.default_mode for the mode. | 68 | /// Calls `openWriteMode` with os.File.default_mode for the mode. |
| 64 | pub fn openWrite(path: []const u8) OpenError!File { | 69 | pub fn openWrite(path: []const u8) OpenError!File { |
| 65 | return openWriteMode(path, os.File.default_mode); | 70 | return openWriteMode(path, os.File.default_mode); |
| ... | @@ -74,19 +79,24 @@ pub const File = struct { | ... | @@ -74,19 +79,24 @@ pub const File = struct { |
| 74 | const fd = try os.posixOpen(path, flags, file_mode); | 79 | const fd = try os.posixOpen(path, flags, file_mode); |
| 75 | return openHandle(fd); | 80 | return openHandle(fd); |
| 76 | } else if (is_windows) { | 81 | } else if (is_windows) { |
| 77 | const handle = try os.windowsOpen( | 82 | const path_w = try windows_util.sliceToPrefixedFileW(path); |
| 78 | path, | 83 | return openWriteModeW(&path_w, file_mode); |
| 79 | windows.GENERIC_WRITE, | ||
| 80 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | ||
| 81 | windows.CREATE_ALWAYS, | ||
| 82 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 83 | ); | ||
| 84 | return openHandle(handle); | ||
| 85 | } else { | 84 | } else { |
| 86 | @compileError("TODO implement openWriteMode for this OS"); | 85 | @compileError("TODO implement openWriteMode for this OS"); |
| 87 | } | 86 | } |
| 88 | } | 87 | } |
| 89 | 88 | ||
| 89 | pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File { | ||
| 90 | const handle = try os.windowsOpenW( | ||
| 91 | path_w, | ||
| 92 | windows.GENERIC_WRITE, | ||
| 93 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | ||
| 94 | windows.CREATE_ALWAYS, | ||
| 95 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 96 | ); | ||
| 97 | return openHandle(handle); | ||
| 98 | } | ||
| 99 | |||
| 90 | /// If the path does not exist it will be created. | 100 | /// If the path does not exist it will be created. |
| 91 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists | 101 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists |
| 92 | /// Call close to clean up. | 102 | /// Call close to clean up. |
| ... | @@ -96,19 +106,24 @@ pub const File = struct { | ... | @@ -96,19 +106,24 @@ pub const File = struct { |
| 96 | const fd = try os.posixOpen(path, flags, file_mode); | 106 | const fd = try os.posixOpen(path, flags, file_mode); |
| 97 | return openHandle(fd); | 107 | return openHandle(fd); |
| 98 | } else if (is_windows) { | 108 | } else if (is_windows) { |
| 99 | const handle = try os.windowsOpen( | 109 | const path_w = try windows_util.sliceToPrefixedFileW(path); |
| 100 | path, | 110 | return openWriteNoClobberW(&path_w, file_mode); |
| 101 | windows.GENERIC_WRITE, | ||
| 102 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | ||
| 103 | windows.CREATE_NEW, | ||
| 104 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 105 | ); | ||
| 106 | return openHandle(handle); | ||
| 107 | } else { | 111 | } else { |
| 108 | @compileError("TODO implement openWriteMode for this OS"); | 112 | @compileError("TODO implement openWriteMode for this OS"); |
| 109 | } | 113 | } |
| 110 | } | 114 | } |
| 111 | 115 | ||
| 116 | pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File { | ||
| 117 | const handle = try os.windowsOpenW( | ||
| 118 | path_w, | ||
| 119 | windows.GENERIC_WRITE, | ||
| 120 | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, | ||
| 121 | windows.CREATE_NEW, | ||
| 122 | windows.FILE_ATTRIBUTE_NORMAL, | ||
| 123 | ); | ||
| 124 | return openHandle(handle); | ||
| 125 | } | ||
| 126 | |||
| 112 | pub fn openHandle(handle: os.FileHandle) File { | 127 | pub fn openHandle(handle: os.FileHandle) File { |
| 113 | return File{ .handle = handle }; | 128 | return File{ .handle = handle }; |
| 114 | } | 129 | } |
| ... | @@ -190,17 +205,16 @@ pub const File = struct { | ... | @@ -190,17 +205,16 @@ pub const File = struct { |
| 190 | 205 | ||
| 191 | /// Upon success, the stream is in an uninitialized state. To continue using it, | 206 | /// Upon success, the stream is in an uninitialized state. To continue using it, |
| 192 | /// you must use the open() function. | 207 | /// you must use the open() function. |
| 193 | pub fn close(self: *File) void { | 208 | pub fn close(self: File) void { |
| 194 | os.close(self.handle); | 209 | os.close(self.handle); |
| 195 | self.handle = undefined; | ||
| 196 | } | 210 | } |
| 197 | 211 | ||
| 198 | /// Calls `os.isTty` on `self.handle`. | 212 | /// Calls `os.isTty` on `self.handle`. |
| 199 | pub fn isTty(self: *File) bool { | 213 | pub fn isTty(self: File) bool { |
| 200 | return os.isTty(self.handle); | 214 | return os.isTty(self.handle); |
| 201 | } | 215 | } |
| 202 | 216 | ||
| 203 | pub fn seekForward(self: *File, amount: isize) !void { | 217 | pub fn seekForward(self: File, amount: isize) !void { |
| 204 | switch (builtin.os) { | 218 | switch (builtin.os) { |
| 205 | Os.linux, Os.macosx, Os.ios => { | 219 | Os.linux, Os.macosx, Os.ios => { |
| 206 | const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); | 220 | const result = posix.lseek(self.handle, amount, posix.SEEK_CUR); |
| ... | @@ -231,7 +245,7 @@ pub const File = struct { | ... | @@ -231,7 +245,7 @@ pub const File = struct { |
| 231 | } | 245 | } |
| 232 | } | 246 | } |
| 233 | 247 | ||
| 234 | pub fn seekTo(self: *File, pos: usize) !void { | 248 | pub fn seekTo(self: File, pos: usize) !void { |
| 235 | switch (builtin.os) { | 249 | switch (builtin.os) { |
| 236 | Os.linux, Os.macosx, Os.ios => { | 250 | Os.linux, Os.macosx, Os.ios => { |
| 237 | const ipos = try math.cast(isize, pos); | 251 | const ipos = try math.cast(isize, pos); |
| ... | @@ -256,6 +270,7 @@ pub const File = struct { | ... | @@ -256,6 +270,7 @@ pub const File = struct { |
| 256 | const err = windows.GetLastError(); | 270 | const err = windows.GetLastError(); |
| 257 | return switch (err) { | 271 | return switch (err) { |
| 258 | windows.ERROR.INVALID_PARAMETER => unreachable, | 272 | windows.ERROR.INVALID_PARAMETER => unreachable, |
| 273 | windows.ERROR.INVALID_HANDLE => unreachable, | ||
| 259 | else => os.unexpectedErrorWindows(err), | 274 | else => os.unexpectedErrorWindows(err), |
| 260 | }; | 275 | }; |
| 261 | } | 276 | } |
| ... | @@ -264,7 +279,7 @@ pub const File = struct { | ... | @@ -264,7 +279,7 @@ pub const File = struct { |
| 264 | } | 279 | } |
| 265 | } | 280 | } |
| 266 | 281 | ||
| 267 | pub fn getPos(self: *File) !usize { | 282 | pub fn getPos(self: File) !usize { |
| 268 | switch (builtin.os) { | 283 | switch (builtin.os) { |
| 269 | Os.linux, Os.macosx, Os.ios => { | 284 | Os.linux, Os.macosx, Os.ios => { |
| 270 | const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); | 285 | const result = posix.lseek(self.handle, 0, posix.SEEK_CUR); |
| ... | @@ -300,7 +315,7 @@ pub const File = struct { | ... | @@ -300,7 +315,7 @@ pub const File = struct { |
| 300 | } | 315 | } |
| 301 | } | 316 | } |
| 302 | 317 | ||
| 303 | pub fn getEndPos(self: *File) !usize { | 318 | pub fn getEndPos(self: File) !usize { |
| 304 | if (is_posix) { | 319 | if (is_posix) { |
| 305 | const stat = try os.posixFStat(self.handle); | 320 | const stat = try os.posixFStat(self.handle); |
| 306 | return @intCast(usize, stat.size); | 321 | return @intCast(usize, stat.size); |
| ... | @@ -325,7 +340,7 @@ pub const File = struct { | ... | @@ -325,7 +340,7 @@ pub const File = struct { |
| 325 | Unexpected, | 340 | Unexpected, |
| 326 | }; | 341 | }; |
| 327 | 342 | ||
| 328 | pub fn mode(self: *File) ModeError!Mode { | 343 | pub fn mode(self: File) ModeError!Mode { |
| 329 | if (is_posix) { | 344 | if (is_posix) { |
| 330 | var stat: posix.Stat = undefined; | 345 | var stat: posix.Stat = undefined; |
| 331 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); | 346 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); |
| ... | @@ -359,7 +374,7 @@ pub const File = struct { | ... | @@ -359,7 +374,7 @@ pub const File = struct { |
| 359 | Unexpected, | 374 | Unexpected, |
| 360 | }; | 375 | }; |
| 361 | 376 | ||
| 362 | pub fn read(self: *File, buffer: []u8) ReadError!usize { | 377 | pub fn read(self: File, buffer: []u8) ReadError!usize { |
| 363 | if (is_posix) { | 378 | if (is_posix) { |
| 364 | var index: usize = 0; | 379 | var index: usize = 0; |
| 365 | while (index < buffer.len) { | 380 | while (index < buffer.len) { |
| ... | @@ -407,7 +422,7 @@ pub const File = struct { | ... | @@ -407,7 +422,7 @@ pub const File = struct { |
| 407 | 422 | ||
| 408 | pub const WriteError = os.WindowsWriteError || os.PosixWriteError; | 423 | pub const WriteError = os.WindowsWriteError || os.PosixWriteError; |
| 409 | 424 | ||
| 410 | pub fn write(self: *File, bytes: []const u8) WriteError!void { | 425 | pub fn write(self: File, bytes: []const u8) WriteError!void { |
| 411 | if (is_posix) { | 426 | if (is_posix) { |
| 412 | try os.posixWrite(self.handle, bytes); | 427 | try os.posixWrite(self.handle, bytes); |
| 413 | } else if (is_windows) { | 428 | } else if (is_windows) { |
std/os/index.zig+136-99| ... | @@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle; | ... | @@ -57,6 +57,7 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle; |
| 57 | pub const windowsWrite = windows_util.windowsWrite; | 57 | pub const windowsWrite = windows_util.windowsWrite; |
| 58 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; | 58 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; |
| 59 | pub const windowsOpen = windows_util.windowsOpen; | 59 | pub const windowsOpen = windows_util.windowsOpen; |
| 60 | pub const windowsOpenW = windows_util.windowsOpenW; | ||
| 60 | pub const windowsLoadDll = windows_util.windowsLoadDll; | 61 | pub const windowsLoadDll = windows_util.windowsLoadDll; |
| 61 | pub const windowsUnloadDll = windows_util.windowsUnloadDll; | 62 | pub const windowsUnloadDll = windows_util.windowsUnloadDll; |
| 62 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; | 63 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; |
| ... | @@ -660,6 +661,7 @@ pub fn getBaseAddress() usize { | ... | @@ -660,6 +661,7 @@ pub fn getBaseAddress() usize { |
| 660 | return phdr - @sizeOf(ElfHeader); | 661 | return phdr - @sizeOf(ElfHeader); |
| 661 | }, | 662 | }, |
| 662 | builtin.Os.macosx => return @ptrToInt(&std.c._mh_execute_header), | 663 | builtin.Os.macosx => return @ptrToInt(&std.c._mh_execute_header), |
| 664 | builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), | ||
| 663 | else => @compileError("Unsupported OS"), | 665 | else => @compileError("Unsupported OS"), |
| 664 | } | 666 | } |
| 665 | } | 667 | } |
| ... | @@ -817,37 +819,40 @@ test "os.getCwd" { | ... | @@ -817,37 +819,40 @@ test "os.getCwd" { |
| 817 | 819 | ||
| 818 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; | 820 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; |
| 819 | 821 | ||
| 820 | pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { | 822 | /// TODO add a symLinkC variant |
| 823 | pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { | ||
| 821 | if (is_windows) { | 824 | if (is_windows) { |
| 822 | return symLinkWindows(allocator, existing_path, new_path); | 825 | return symLinkWindows(existing_path, new_path); |
| 823 | } else { | 826 | } else { |
| 824 | return symLinkPosix(allocator, existing_path, new_path); | 827 | return symLinkPosix(existing_path, new_path); |
| 825 | } | 828 | } |
| 826 | } | 829 | } |
| 827 | 830 | ||
| 828 | pub const WindowsSymLinkError = error{ | 831 | pub const WindowsSymLinkError = error{ |
| 829 | OutOfMemory, | 832 | NameTooLong, |
| 833 | InvalidUtf8, | ||
| 834 | BadPathName, | ||
| 830 | 835 | ||
| 831 | /// See https://github.com/ziglang/zig/issues/1396 | 836 | /// See https://github.com/ziglang/zig/issues/1396 |
| 832 | Unexpected, | 837 | Unexpected, |
| 833 | }; | 838 | }; |
| 834 | 839 | ||
| 835 | pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { | 840 | pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { |
| 836 | const existing_with_null = try cstr.addNullByte(allocator, existing_path); | 841 | if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { |
| 837 | defer allocator.free(existing_with_null); | ||
| 838 | const new_with_null = try cstr.addNullByte(allocator, new_path); | ||
| 839 | defer allocator.free(new_with_null); | ||
| 840 | |||
| 841 | if (windows.CreateSymbolicLinkA(existing_with_null.ptr, new_with_null.ptr, 0) == 0) { | ||
| 842 | const err = windows.GetLastError(); | 842 | const err = windows.GetLastError(); |
| 843 | return switch (err) { | 843 | switch (err) { |
| 844 | else => unexpectedErrorWindows(err), | 844 | else => return unexpectedErrorWindows(err), |
| 845 | }; | 845 | } |
| 846 | } | 846 | } |
| 847 | } | 847 | } |
| 848 | 848 | ||
| 849 | pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { | ||
| 850 | const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); | ||
| 851 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | ||
| 852 | return symLinkW(&existing_path_w, &new_path_w); | ||
| 853 | } | ||
| 854 | |||
| 849 | pub const PosixSymLinkError = error{ | 855 | pub const PosixSymLinkError = error{ |
| 850 | OutOfMemory, | ||
| 851 | AccessDenied, | 856 | AccessDenied, |
| 852 | DiskQuota, | 857 | DiskQuota, |
| 853 | PathAlreadyExists, | 858 | PathAlreadyExists, |
| ... | @@ -864,43 +869,40 @@ pub const PosixSymLinkError = error{ | ... | @@ -864,43 +869,40 @@ pub const PosixSymLinkError = error{ |
| 864 | Unexpected, | 869 | Unexpected, |
| 865 | }; | 870 | }; |
| 866 | 871 | ||
| 867 | pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { | 872 | pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { |
| 868 | const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2); | 873 | const err = posix.getErrno(posix.symlink(existing_path, new_path)); |
| 869 | defer allocator.free(full_buf); | 874 | switch (err) { |
| 870 | 875 | 0 => return, | |
| 871 | const existing_buf = full_buf; | 876 | posix.EFAULT => unreachable, |
| 872 | mem.copy(u8, existing_buf, existing_path); | 877 | posix.EINVAL => unreachable, |
| 873 | existing_buf[existing_path.len] = 0; | 878 | posix.EACCES => return error.AccessDenied, |
| 874 | 879 | posix.EPERM => return error.AccessDenied, | |
| 875 | const new_buf = full_buf[existing_path.len + 1 ..]; | 880 | posix.EDQUOT => return error.DiskQuota, |
| 876 | mem.copy(u8, new_buf, new_path); | 881 | posix.EEXIST => return error.PathAlreadyExists, |
| 877 | new_buf[new_path.len] = 0; | 882 | posix.EIO => return error.FileSystem, |
| 878 | 883 | posix.ELOOP => return error.SymLinkLoop, | |
| 879 | const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr)); | 884 | posix.ENAMETOOLONG => return error.NameTooLong, |
| 880 | if (err > 0) { | 885 | posix.ENOENT => return error.FileNotFound, |
| 881 | return switch (err) { | 886 | posix.ENOTDIR => return error.NotDir, |
| 882 | posix.EFAULT, posix.EINVAL => unreachable, | 887 | posix.ENOMEM => return error.SystemResources, |
| 883 | posix.EACCES, posix.EPERM => error.AccessDenied, | 888 | posix.ENOSPC => return error.NoSpaceLeft, |
| 884 | posix.EDQUOT => error.DiskQuota, | 889 | posix.EROFS => return error.ReadOnlyFileSystem, |
| 885 | posix.EEXIST => error.PathAlreadyExists, | 890 | else => return unexpectedErrorPosix(err), |
| 886 | posix.EIO => error.FileSystem, | ||
| 887 | posix.ELOOP => error.SymLinkLoop, | ||
| 888 | posix.ENAMETOOLONG => error.NameTooLong, | ||
| 889 | posix.ENOENT => error.FileNotFound, | ||
| 890 | posix.ENOTDIR => error.NotDir, | ||
| 891 | posix.ENOMEM => error.SystemResources, | ||
| 892 | posix.ENOSPC => error.NoSpaceLeft, | ||
| 893 | posix.EROFS => error.ReadOnlyFileSystem, | ||
| 894 | else => unexpectedErrorPosix(err), | ||
| 895 | }; | ||
| 896 | } | 891 | } |
| 897 | } | 892 | } |
| 898 | 893 | ||
| 894 | pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { | ||
| 895 | const existing_path_c = try toPosixPath(existing_path); | ||
| 896 | const new_path_c = try toPosixPath(new_path); | ||
| 897 | return symLinkPosixC(&existing_path_c, &new_path_c); | ||
| 898 | } | ||
| 899 | |||
| 899 | // here we replace the standard +/ with -_ so that it can be used in a file name | 900 | // here we replace the standard +/ with -_ so that it can be used in a file name |
| 900 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); | 901 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); |
| 901 | 902 | ||
| 903 | /// TODO remove the allocator requirement from this API | ||
| 902 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { | 904 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { |
| 903 | if (symLink(allocator, existing_path, new_path)) { | 905 | if (symLink(existing_path, new_path)) { |
| 904 | return; | 906 | return; |
| 905 | } else |err| switch (err) { | 907 | } else |err| switch (err) { |
| 906 | error.PathAlreadyExists => {}, | 908 | error.PathAlreadyExists => {}, |
| ... | @@ -918,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: | ... | @@ -918,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 918 | try getRandomBytes(rand_buf[0..]); | 920 | try getRandomBytes(rand_buf[0..]); |
| 919 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); | 921 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); |
| 920 | 922 | ||
| 921 | if (symLink(allocator, existing_path, tmp_path)) { | 923 | if (symLink(existing_path, tmp_path)) { |
| 922 | return rename(tmp_path, new_path); | 924 | return rename(tmp_path, new_path); |
| 923 | } else |err| switch (err) { | 925 | } else |err| switch (err) { |
| 924 | error.PathAlreadyExists => continue, | 926 | error.PathAlreadyExists => continue, |
| ... | @@ -1250,49 +1252,65 @@ pub const DeleteDirError = error{ | ... | @@ -1250,49 +1252,65 @@ pub const DeleteDirError = error{ |
| 1250 | NotDir, | 1252 | NotDir, |
| 1251 | DirNotEmpty, | 1253 | DirNotEmpty, |
| 1252 | ReadOnlyFileSystem, | 1254 | ReadOnlyFileSystem, |
| 1253 | OutOfMemory, | 1255 | InvalidUtf8, |
| 1256 | BadPathName, | ||
| 1254 | 1257 | ||
| 1255 | /// See https://github.com/ziglang/zig/issues/1396 | 1258 | /// See https://github.com/ziglang/zig/issues/1396 |
| 1256 | Unexpected, | 1259 | Unexpected, |
| 1257 | }; | 1260 | }; |
| 1258 | 1261 | ||
| 1259 | /// Returns ::error.DirNotEmpty if the directory is not empty. | 1262 | pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { |
| 1260 | /// To delete a directory recursively, see ::deleteTree | 1263 | switch (builtin.os) { |
| 1261 | pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void { | 1264 | Os.windows => { |
| 1262 | const path_buf = try allocator.alloc(u8, dir_path.len + 1); | 1265 | const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); |
| 1263 | defer allocator.free(path_buf); | 1266 | return deleteDirW(&dir_path_w); |
| 1267 | }, | ||
| 1268 | Os.linux, Os.macosx, Os.ios => { | ||
| 1269 | const err = posix.getErrno(posix.rmdir(dir_path)); | ||
| 1270 | switch (err) { | ||
| 1271 | 0 => return, | ||
| 1272 | posix.EACCES => return error.AccessDenied, | ||
| 1273 | posix.EPERM => return error.AccessDenied, | ||
| 1274 | posix.EBUSY => return error.FileBusy, | ||
| 1275 | posix.EFAULT => unreachable, | ||
| 1276 | posix.EINVAL => unreachable, | ||
| 1277 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1278 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1279 | posix.ENOENT => return error.FileNotFound, | ||
| 1280 | posix.ENOMEM => return error.SystemResources, | ||
| 1281 | posix.ENOTDIR => return error.NotDir, | ||
| 1282 | posix.EEXIST => return error.DirNotEmpty, | ||
| 1283 | posix.ENOTEMPTY => return error.DirNotEmpty, | ||
| 1284 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1285 | else => return unexpectedErrorPosix(err), | ||
| 1286 | } | ||
| 1287 | }, | ||
| 1288 | else => @compileError("unimplemented"), | ||
| 1289 | } | ||
| 1290 | } | ||
| 1264 | 1291 | ||
| 1265 | mem.copy(u8, path_buf, dir_path); | 1292 | pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { |
| 1266 | path_buf[dir_path.len] = 0; | 1293 | if (windows.RemoveDirectoryW(dir_path_w) == 0) { |
| 1294 | const err = windows.GetLastError(); | ||
| 1295 | switch (err) { | ||
| 1296 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 1297 | windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, | ||
| 1298 | else => return unexpectedErrorWindows(err), | ||
| 1299 | } | ||
| 1300 | } | ||
| 1301 | } | ||
| 1267 | 1302 | ||
| 1303 | /// Returns ::error.DirNotEmpty if the directory is not empty. | ||
| 1304 | /// To delete a directory recursively, see ::deleteTree | ||
| 1305 | pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { | ||
| 1268 | switch (builtin.os) { | 1306 | switch (builtin.os) { |
| 1269 | Os.windows => { | 1307 | Os.windows => { |
| 1270 | if (windows.RemoveDirectoryA(path_buf.ptr) == 0) { | 1308 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); |
| 1271 | const err = windows.GetLastError(); | 1309 | return deleteDirW(&dir_path_w); |
| 1272 | return switch (err) { | ||
| 1273 | windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, | ||
| 1274 | windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty, | ||
| 1275 | else => unexpectedErrorWindows(err), | ||
| 1276 | }; | ||
| 1277 | } | ||
| 1278 | }, | 1310 | }, |
| 1279 | Os.linux, Os.macosx, Os.ios => { | 1311 | Os.linux, Os.macosx, Os.ios => { |
| 1280 | const err = posix.getErrno(posix.rmdir(path_buf.ptr)); | 1312 | const dir_path_c = try toPosixPath(dir_path); |
| 1281 | if (err > 0) { | 1313 | return deleteDirC(&dir_path_c); |
| 1282 | return switch (err) { | ||
| 1283 | posix.EACCES, posix.EPERM => error.AccessDenied, | ||
| 1284 | posix.EBUSY => error.FileBusy, | ||
| 1285 | posix.EFAULT, posix.EINVAL => unreachable, | ||
| 1286 | posix.ELOOP => error.SymLinkLoop, | ||
| 1287 | posix.ENAMETOOLONG => error.NameTooLong, | ||
| 1288 | posix.ENOENT => error.FileNotFound, | ||
| 1289 | posix.ENOMEM => error.SystemResources, | ||
| 1290 | posix.ENOTDIR => error.NotDir, | ||
| 1291 | posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty, | ||
| 1292 | posix.EROFS => error.ReadOnlyFileSystem, | ||
| 1293 | else => unexpectedErrorPosix(err), | ||
| 1294 | }; | ||
| 1295 | } | ||
| 1296 | }, | 1314 | }, |
| 1297 | else => @compileError("unimplemented"), | 1315 | else => @compileError("unimplemented"), |
| 1298 | } | 1316 | } |
| ... | @@ -1344,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! | ... | @@ -1344,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1344 | error.IsDir => {}, | 1362 | error.IsDir => {}, |
| 1345 | error.AccessDenied => got_access_denied = true, | 1363 | error.AccessDenied => got_access_denied = true, |
| 1346 | 1364 | ||
| 1365 | error.InvalidUtf8, | ||
| 1347 | error.SymLinkLoop, | 1366 | error.SymLinkLoop, |
| 1348 | error.NameTooLong, | 1367 | error.NameTooLong, |
| 1349 | error.SystemResources, | 1368 | error.SystemResources, |
| ... | @@ -1351,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! | ... | @@ -1351,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1351 | error.NotDir, | 1370 | error.NotDir, |
| 1352 | error.FileSystem, | 1371 | error.FileSystem, |
| 1353 | error.FileBusy, | 1372 | error.FileBusy, |
| 1354 | error.InvalidUtf8, | ||
| 1355 | error.BadPathName, | 1373 | error.BadPathName, |
| 1356 | error.Unexpected, | 1374 | error.Unexpected, |
| 1357 | => return err, | 1375 | => return err, |
| ... | @@ -1379,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! | ... | @@ -1379,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1379 | error.NoSpaceLeft, | 1397 | error.NoSpaceLeft, |
| 1380 | error.PathAlreadyExists, | 1398 | error.PathAlreadyExists, |
| 1381 | error.Unexpected, | 1399 | error.Unexpected, |
| 1400 | error.InvalidUtf8, | ||
| 1401 | error.BadPathName, | ||
| 1382 | => return err, | 1402 | => return err, |
| 1383 | }; | 1403 | }; |
| 1384 | defer dir.close(); | 1404 | defer dir.close(); |
| ... | @@ -1396,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! | ... | @@ -1396,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1396 | try deleteTree(allocator, full_entry_path); | 1416 | try deleteTree(allocator, full_entry_path); |
| 1397 | } | 1417 | } |
| 1398 | } | 1418 | } |
| 1399 | return deleteDir(allocator, full_path); | 1419 | return deleteDir(full_path); |
| 1400 | } | 1420 | } |
| 1401 | } | 1421 | } |
| 1402 | 1422 | ||
| ... | @@ -1420,8 +1440,9 @@ pub const Dir = struct { | ... | @@ -1420,8 +1440,9 @@ pub const Dir = struct { |
| 1420 | }, | 1440 | }, |
| 1421 | Os.windows => struct { | 1441 | Os.windows => struct { |
| 1422 | handle: windows.HANDLE, | 1442 | handle: windows.HANDLE, |
| 1423 | find_file_data: windows.WIN32_FIND_DATAA, | 1443 | find_file_data: windows.WIN32_FIND_DATAW, |
| 1424 | first: bool, | 1444 | first: bool, |
| 1445 | name_data: [256]u8, | ||
| 1425 | }, | 1446 | }, |
| 1426 | else => @compileError("unimplemented"), | 1447 | else => @compileError("unimplemented"), |
| 1427 | }; | 1448 | }; |
| ... | @@ -1458,6 +1479,8 @@ pub const Dir = struct { | ... | @@ -1458,6 +1479,8 @@ pub const Dir = struct { |
| 1458 | NoSpaceLeft, | 1479 | NoSpaceLeft, |
| 1459 | PathAlreadyExists, | 1480 | PathAlreadyExists, |
| 1460 | OutOfMemory, | 1481 | OutOfMemory, |
| 1482 | InvalidUtf8, | ||
| 1483 | BadPathName, | ||
| 1461 | 1484 | ||
| 1462 | /// See https://github.com/ziglang/zig/issues/1396 | 1485 | /// See https://github.com/ziglang/zig/issues/1396 |
| 1463 | Unexpected, | 1486 | Unexpected, |
| ... | @@ -1469,12 +1492,13 @@ pub const Dir = struct { | ... | @@ -1469,12 +1492,13 @@ pub const Dir = struct { |
| 1469 | .allocator = allocator, | 1492 | .allocator = allocator, |
| 1470 | .handle = switch (builtin.os) { | 1493 | .handle = switch (builtin.os) { |
| 1471 | Os.windows => blk: { | 1494 | Os.windows => blk: { |
| 1472 | var find_file_data: windows.WIN32_FIND_DATAA = undefined; | 1495 | var find_file_data: windows.WIN32_FIND_DATAW = undefined; |
| 1473 | const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data); | 1496 | const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data); |
| 1474 | break :blk Handle{ | 1497 | break :blk Handle{ |
| 1475 | .handle = handle, | 1498 | .handle = handle, |
| 1476 | .find_file_data = find_file_data, // TODO guaranteed copy elision | 1499 | .find_file_data = find_file_data, // TODO guaranteed copy elision |
| 1477 | .first = true, | 1500 | .first = true, |
| 1501 | .name_data = undefined, | ||
| 1478 | }; | 1502 | }; |
| 1479 | }, | 1503 | }, |
| 1480 | Os.macosx, Os.ios => Handle{ | 1504 | Os.macosx, Os.ios => Handle{ |
| ... | @@ -1589,9 +1613,12 @@ pub const Dir = struct { | ... | @@ -1589,9 +1613,12 @@ pub const Dir = struct { |
| 1589 | if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) | 1613 | if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) |
| 1590 | return null; | 1614 | return null; |
| 1591 | } | 1615 | } |
| 1592 | const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr); | 1616 | const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); |
| 1593 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) | 1617 | if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'})) |
| 1594 | continue; | 1618 | continue; |
| 1619 | // Trust that Windows gives us valid UTF-16LE | ||
| 1620 | const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; | ||
| 1621 | const name_utf8 = self.handle.name_data[0..name_utf8_len]; | ||
| 1595 | const kind = blk: { | 1622 | const kind = blk: { |
| 1596 | const attrs = self.handle.find_file_data.dwFileAttributes; | 1623 | const attrs = self.handle.find_file_data.dwFileAttributes; |
| 1597 | if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; | 1624 | if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; |
| ... | @@ -1600,7 +1627,7 @@ pub const Dir = struct { | ... | @@ -1600,7 +1627,7 @@ pub const Dir = struct { |
| 1600 | break :blk Entry.Kind.Unknown; | 1627 | break :blk Entry.Kind.Unknown; |
| 1601 | }; | 1628 | }; |
| 1602 | return Entry{ | 1629 | return Entry{ |
| 1603 | .name = name, | 1630 | .name = name_utf8, |
| 1604 | .kind = kind, | 1631 | .kind = kind, |
| 1605 | }; | 1632 | }; |
| 1606 | } | 1633 | } |
| ... | @@ -2087,8 +2114,9 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { | ... | @@ -2087,8 +2114,9 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { |
| 2087 | /// Call this when you made a windows DLL call or something that does SetLastError | 2114 | /// Call this when you made a windows DLL call or something that does SetLastError |
| 2088 | /// and you get an unexpected error. | 2115 | /// and you get an unexpected error. |
| 2089 | pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { | 2116 | pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { |
| 2090 | if (true) { | 2117 | if (unexpected_error_tracing) { |
| 2091 | debug.warn("unexpected GetLastError(): {}\n", err); | 2118 | debug.warn("unexpected GetLastError(): {}\n", err); |
| 2119 | @breakpoint(); | ||
| 2092 | debug.dumpCurrentStackTrace(null); | 2120 | debug.dumpCurrentStackTrace(null); |
| 2093 | } | 2121 | } |
| 2094 | return error.Unexpected; | 2122 | return error.Unexpected; |
| ... | @@ -2103,17 +2131,35 @@ pub fn openSelfExe() !os.File { | ... | @@ -2103,17 +2131,35 @@ pub fn openSelfExe() !os.File { |
| 2103 | buf[self_exe_path.len] = 0; | 2131 | buf[self_exe_path.len] = 0; |
| 2104 | return os.File.openReadC(self_exe_path.ptr); | 2132 | return os.File.openReadC(self_exe_path.ptr); |
| 2105 | }, | 2133 | }, |
| 2134 | Os.windows => { | ||
| 2135 | var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 2136 | const wide_slice = try selfExePathW(&buf); | ||
| 2137 | return os.File.openReadW(wide_slice.ptr); | ||
| 2138 | }, | ||
| 2106 | else => @compileError("Unsupported OS"), | 2139 | else => @compileError("Unsupported OS"), |
| 2107 | } | 2140 | } |
| 2108 | } | 2141 | } |
| 2109 | 2142 | ||
| 2110 | test "openSelfExe" { | 2143 | test "openSelfExe" { |
| 2111 | switch (builtin.os) { | 2144 | switch (builtin.os) { |
| 2112 | Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), | 2145 | Os.linux, Os.macosx, Os.ios, Os.windows => (try openSelfExe()).close(), |
| 2113 | else => return error.SkipZigTest, // Unsupported OS | 2146 | else => return error.SkipZigTest, // Unsupported OS. |
| 2114 | } | 2147 | } |
| 2115 | } | 2148 | } |
| 2116 | 2149 | ||
| 2150 | pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { | ||
| 2151 | const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast | ||
| 2152 | const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); | ||
| 2153 | assert(rc <= out_buffer.len); | ||
| 2154 | if (rc == 0) { | ||
| 2155 | const err = windows.GetLastError(); | ||
| 2156 | switch (err) { | ||
| 2157 | else => return unexpectedErrorWindows(err), | ||
| 2158 | } | ||
| 2159 | } | ||
| 2160 | return out_buffer[0..rc]; | ||
| 2161 | } | ||
| 2162 | |||
| 2117 | /// Get the path to the current executable. | 2163 | /// Get the path to the current executable. |
| 2118 | /// If you only need the directory, use selfExeDirPath. | 2164 | /// If you only need the directory, use selfExeDirPath. |
| 2119 | /// If you only want an open file handle, use openSelfExe. | 2165 | /// If you only want an open file handle, use openSelfExe. |
| ... | @@ -2129,16 +2175,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ... | @@ -2129,16 +2175,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 2129 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), | 2175 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), |
| 2130 | Os.windows => { | 2176 | Os.windows => { |
| 2131 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | 2177 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; |
| 2132 | const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast | 2178 | const utf16le_slice = try selfExePathW(&utf16le_buf); |
| 2133 | const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len); | ||
| 2134 | assert(rc <= utf16le_buf.len); | ||
| 2135 | if (rc == 0) { | ||
| 2136 | const err = windows.GetLastError(); | ||
| 2137 | switch (err) { | ||
| 2138 | else => return unexpectedErrorWindows(err), | ||
| 2139 | } | ||
| 2140 | } | ||
| 2141 | const utf16le_slice = utf16le_buf[0..rc]; | ||
| 2142 | // Trust that Windows gives us valid UTF-16LE. | 2179 | // Trust that Windows gives us valid UTF-16LE. |
| 2143 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | 2180 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; |
| 2144 | return out_buffer[0..end_index]; | 2181 | return out_buffer[0..end_index]; |
std/os/windows/advapi32.zig+16-5| ... | @@ -23,11 +23,22 @@ pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlag | ... | @@ -23,11 +23,22 @@ pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlag |
| 23 | 23 | ||
| 24 | pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; | 24 | pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL; |
| 25 | 25 | ||
| 26 | pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM, | 26 | pub extern "advapi32" stdcallcc fn RegOpenKeyExW( |
| 27 | phkResult: &HKEY,) LSTATUS; | 27 | hKey: HKEY, |
| 28 | 28 | lpSubKey: LPCWSTR, | |
| 29 | pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, | 29 | ulOptions: DWORD, |
| 30 | lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; | 30 | samDesired: REGSAM, |
| 31 | phkResult: &HKEY, | ||
| 32 | ) LSTATUS; | ||
| 33 | |||
| 34 | pub extern "advapi32" stdcallcc fn RegQueryValueExW( | ||
| 35 | hKey: HKEY, | ||
| 36 | lpValueName: LPCWSTR, | ||
| 37 | lpReserved: LPDWORD, | ||
| 38 | lpType: LPDWORD, | ||
| 39 | lpData: LPBYTE, | ||
| 40 | lpcbData: LPDWORD, | ||
| 41 | ) LSTATUS; | ||
| 31 | 42 | ||
| 32 | // RtlGenRandom is known as SystemFunction036 under advapi32 | 43 | // RtlGenRandom is known as SystemFunction036 under advapi32 |
| 33 | // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ | 44 | // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ |
std/os/windows/index.zig+29-15| ... | @@ -3,10 +3,9 @@ const assert = std.debug.assert; | ... | @@ -3,10 +3,9 @@ const assert = std.debug.assert; |
| 3 | 3 | ||
| 4 | pub use @import("advapi32.zig"); | 4 | pub use @import("advapi32.zig"); |
| 5 | pub use @import("kernel32.zig"); | 5 | pub use @import("kernel32.zig"); |
| 6 | pub use @import("ntdll.zig"); | ||
| 6 | pub use @import("ole32.zig"); | 7 | pub use @import("ole32.zig"); |
| 7 | pub use @import("shell32.zig"); | 8 | pub use @import("shell32.zig"); |
| 8 | pub use @import("shlwapi.zig"); | ||
| 9 | pub use @import("user32.zig"); | ||
| 10 | 9 | ||
| 11 | test "import" { | 10 | test "import" { |
| 12 | _ = @import("util.zig"); | 11 | _ = @import("util.zig"); |
| ... | @@ -14,6 +13,7 @@ test "import" { | ... | @@ -14,6 +13,7 @@ test "import" { |
| 14 | 13 | ||
| 15 | pub const ERROR = @import("error.zig"); | 14 | pub const ERROR = @import("error.zig"); |
| 16 | 15 | ||
| 16 | pub const SHORT = c_short; | ||
| 17 | pub const BOOL = c_int; | 17 | pub const BOOL = c_int; |
| 18 | pub const BOOLEAN = BYTE; | 18 | pub const BOOLEAN = BYTE; |
| 19 | pub const BYTE = u8; | 19 | pub const BYTE = u8; |
| ... | @@ -172,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct { | ... | @@ -172,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct { |
| 172 | dwThreadId: DWORD, | 172 | dwThreadId: DWORD, |
| 173 | }; | 173 | }; |
| 174 | 174 | ||
| 175 | pub const STARTUPINFOA = extern struct { | 175 | pub const STARTUPINFOW = extern struct { |
| 176 | cb: DWORD, | 176 | cb: DWORD, |
| 177 | lpReserved: ?LPSTR, | 177 | lpReserved: ?LPWSTR, |
| 178 | lpDesktop: ?LPSTR, | 178 | lpDesktop: ?LPWSTR, |
| 179 | lpTitle: ?LPSTR, | 179 | lpTitle: ?LPWSTR, |
| 180 | dwX: DWORD, | 180 | dwX: DWORD, |
| 181 | dwY: DWORD, | 181 | dwY: DWORD, |
| 182 | dwXSize: DWORD, | 182 | dwXSize: DWORD, |
| ... | @@ -236,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001; | ... | @@ -236,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001; |
| 236 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; | 236 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; |
| 237 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; | 237 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; |
| 238 | 238 | ||
| 239 | pub const WIN32_FIND_DATAA = extern struct { | 239 | pub const WIN32_FIND_DATAW = extern struct { |
| 240 | dwFileAttributes: DWORD, | 240 | dwFileAttributes: DWORD, |
| 241 | ftCreationTime: FILETIME, | 241 | ftCreationTime: FILETIME, |
| 242 | ftLastAccessTime: FILETIME, | 242 | ftLastAccessTime: FILETIME, |
| ... | @@ -245,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct { | ... | @@ -245,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct { |
| 245 | nFileSizeLow: DWORD, | 245 | nFileSizeLow: DWORD, |
| 246 | dwReserved0: DWORD, | 246 | dwReserved0: DWORD, |
| 247 | dwReserved1: DWORD, | 247 | dwReserved1: DWORD, |
| 248 | cFileName: [260]CHAR, | 248 | cFileName: [260]u16, |
| 249 | cAlternateFileName: [14]CHAR, | 249 | cAlternateFileName: [14]u16, |
| 250 | }; | 250 | }; |
| 251 | 251 | ||
| 252 | pub const FILETIME = extern struct { | 252 | pub const FILETIME = extern struct { |
| ... | @@ -288,27 +288,27 @@ pub const GUID = extern struct { | ... | @@ -288,27 +288,27 @@ pub const GUID = extern struct { |
| 288 | assert(str[index] == '{'); | 288 | assert(str[index] == '{'); |
| 289 | index += 1; | 289 | index += 1; |
| 290 | 290 | ||
| 291 | guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index..index + 8], 16) catch unreachable; | 291 | guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable; |
| 292 | index += 8; | 292 | index += 8; |
| 293 | 293 | ||
| 294 | assert(str[index] == '-'); | 294 | assert(str[index] == '-'); |
| 295 | index += 1; | 295 | index += 1; |
| 296 | 296 | ||
| 297 | guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable; | 297 | guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; |
| 298 | index += 4; | 298 | index += 4; |
| 299 | 299 | ||
| 300 | assert(str[index] == '-'); | 300 | assert(str[index] == '-'); |
| 301 | index += 1; | 301 | index += 1; |
| 302 | 302 | ||
| 303 | guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index..index + 4], 16) catch unreachable; | 303 | guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; |
| 304 | index += 4; | 304 | index += 4; |
| 305 | 305 | ||
| 306 | assert(str[index] == '-'); | 306 | assert(str[index] == '-'); |
| 307 | index += 1; | 307 | index += 1; |
| 308 | 308 | ||
| 309 | guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; | 309 | guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; |
| 310 | index += 2; | 310 | index += 2; |
| 311 | guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; | 311 | guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; |
| 312 | index += 2; | 312 | index += 2; |
| 313 | 313 | ||
| 314 | assert(str[index] == '-'); | 314 | assert(str[index] == '-'); |
| ... | @@ -316,7 +316,7 @@ pub const GUID = extern struct { | ... | @@ -316,7 +316,7 @@ pub const GUID = extern struct { |
| 316 | 316 | ||
| 317 | var i: usize = 2; | 317 | var i: usize = 2; |
| 318 | while (i < guid.Data4.len) : (i += 1) { | 318 | while (i < guid.Data4.len) : (i += 1) { |
| 319 | guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index..index + 2], 16) catch unreachable; | 319 | guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; |
| 320 | index += 2; | 320 | index += 2; |
| 321 | } | 321 | } |
| 322 | 322 | ||
| ... | @@ -363,3 +363,17 @@ pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; | ... | @@ -363,3 +363,17 @@ pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; |
| 363 | pub const FILE_FLAG_SESSION_AWARE = 0x00800000; | 363 | pub const FILE_FLAG_SESSION_AWARE = 0x00800000; |
| 364 | pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; | 364 | pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; |
| 365 | pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; | 365 | pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; |
| 366 | |||
| 367 | pub const SMALL_RECT = extern struct { | ||
| 368 | Left: SHORT, | ||
| 369 | Top: SHORT, | ||
| 370 | Right: SHORT, | ||
| 371 | Bottom: SHORT, | ||
| 372 | }; | ||
| 373 | |||
| 374 | pub const COORD = extern struct { | ||
| 375 | X: SHORT, | ||
| 376 | Y: SHORT, | ||
| 377 | }; | ||
| 378 | |||
| 379 | pub const CREATE_UNICODE_ENVIRONMENT = 1024; |
std/os/windows/kernel32.zig+30-43| ... | @@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE | ... | @@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE |
| 4 | 4 | ||
| 5 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; | 5 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; |
| 6 | 6 | ||
| 7 | pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; | ||
| 8 | pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; | 7 | pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; |
| 9 | 8 | ||
| 10 | pub extern "kernel32" stdcallcc fn CreateFileA( | ||
| 11 | lpFileName: [*]const u8, // TODO null terminated pointer type | ||
| 12 | dwDesiredAccess: DWORD, | ||
| 13 | dwShareMode: DWORD, | ||
| 14 | lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, | ||
| 15 | dwCreationDisposition: DWORD, | ||
| 16 | dwFlagsAndAttributes: DWORD, | ||
| 17 | hTemplateFile: ?HANDLE, | ||
| 18 | ) HANDLE; | ||
| 19 | |||
| 20 | pub extern "kernel32" stdcallcc fn CreateFileW( | 9 | pub extern "kernel32" stdcallcc fn CreateFileW( |
| 21 | lpFileName: [*]const u16, // TODO null terminated pointer type | 10 | lpFileName: [*]const u16, // TODO null terminated pointer type |
| 22 | dwDesiredAccess: DWORD, | 11 | dwDesiredAccess: DWORD, |
| ... | @@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe( | ... | @@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe( |
| 34 | nSize: DWORD, | 23 | nSize: DWORD, |
| 35 | ) BOOL; | 24 | ) BOOL; |
| 36 | 25 | ||
| 37 | pub extern "kernel32" stdcallcc fn CreateProcessA( | 26 | pub extern "kernel32" stdcallcc fn CreateProcessW( |
| 38 | lpApplicationName: ?LPCSTR, | 27 | lpApplicationName: ?LPWSTR, |
| 39 | lpCommandLine: LPSTR, | 28 | lpCommandLine: LPWSTR, |
| 40 | lpProcessAttributes: ?*SECURITY_ATTRIBUTES, | 29 | lpProcessAttributes: ?*SECURITY_ATTRIBUTES, |
| 41 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, | 30 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, |
| 42 | bInheritHandles: BOOL, | 31 | bInheritHandles: BOOL, |
| 43 | dwCreationFlags: DWORD, | 32 | dwCreationFlags: DWORD, |
| 44 | lpEnvironment: ?*c_void, | 33 | lpEnvironment: ?*c_void, |
| 45 | lpCurrentDirectory: ?LPCSTR, | 34 | lpCurrentDirectory: ?LPWSTR, |
| 46 | lpStartupInfo: *STARTUPINFOA, | 35 | lpStartupInfo: *STARTUPINFOW, |
| 47 | lpProcessInformation: *PROCESS_INFORMATION, | 36 | lpProcessInformation: *PROCESS_INFORMATION, |
| 48 | ) BOOL; | 37 | ) BOOL; |
| 49 | 38 | ||
| 50 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( | 39 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN; |
| 51 | lpSymlinkFileName: LPCSTR, | ||
| 52 | lpTargetFileName: LPCSTR, | ||
| 53 | dwFlags: DWORD, | ||
| 54 | ) BOOLEAN; | ||
| 55 | 40 | ||
| 56 | pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; | 41 | pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; |
| 57 | 42 | ||
| 58 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; | 43 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; |
| 59 | 44 | ||
| 60 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL; | ||
| 61 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; | 45 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; |
| 62 | 46 | ||
| 63 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; | 47 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; |
| 64 | 48 | ||
| 65 | pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; | 49 | pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE; |
| 66 | pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; | 50 | pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; |
| 67 | pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; | 51 | pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL; |
| 68 | 52 | ||
| 69 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; | 53 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; |
| 70 | 54 | ||
| ... | @@ -72,7 +56,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; | ... | @@ -72,7 +56,8 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; |
| 72 | 56 | ||
| 73 | pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; | 57 | pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL; |
| 74 | 58 | ||
| 75 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD; | 59 | pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL; |
| 60 | |||
| 76 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; | 61 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; |
| 77 | 62 | ||
| 78 | pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; | 63 | pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; |
| ... | @@ -86,12 +71,12 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo | ... | @@ -86,12 +71,12 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo |
| 86 | 71 | ||
| 87 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; | 72 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; |
| 88 | 73 | ||
| 89 | pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD; | ||
| 90 | pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD; | 74 | pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD; |
| 91 | 75 | ||
| 92 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD; | ||
| 93 | pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; | 76 | pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; |
| 94 | 77 | ||
| 78 | pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE; | ||
| 79 | |||
| 95 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; | 80 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; |
| 96 | 81 | ||
| 97 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( | 82 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( |
| ... | @@ -101,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( | ... | @@ -101,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( |
| 101 | in_dwBufferSize: DWORD, | 86 | in_dwBufferSize: DWORD, |
| 102 | ) BOOL; | 87 | ) BOOL; |
| 103 | 88 | ||
| 104 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( | ||
| 105 | hFile: HANDLE, | ||
| 106 | lpszFilePath: LPSTR, | ||
| 107 | cchFilePath: DWORD, | ||
| 108 | dwFlags: DWORD, | ||
| 109 | ) DWORD; | ||
| 110 | |||
| 111 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW( | 89 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW( |
| 112 | hFile: HANDLE, | 90 | hFile: HANDLE, |
| 113 | lpszFilePath: [*]u16, | 91 | lpszFilePath: [*]u16, |
| ... | @@ -138,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem | ... | @@ -138,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem |
| 138 | 116 | ||
| 139 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL; | 117 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL; |
| 140 | 118 | ||
| 141 | pub extern "kernel32" stdcallcc fn MoveFileExA( | ||
| 142 | lpExistingFileName: [*]const u8, | ||
| 143 | lpNewFileName: [*]const u8, | ||
| 144 | dwFlags: DWORD, | ||
| 145 | ) BOOL; | ||
| 146 | |||
| 147 | pub extern "kernel32" stdcallcc fn MoveFileExW( | 119 | pub extern "kernel32" stdcallcc fn MoveFileExW( |
| 148 | lpExistingFileName: [*]const u16, | 120 | lpExistingFileName: [*]const u16, |
| 149 | lpNewFileName: [*]const u16, | 121 | lpNewFileName: [*]const u16, |
| ... | @@ -175,7 +147,9 @@ pub extern "kernel32" stdcallcc fn ReadFile( | ... | @@ -175,7 +147,9 @@ pub extern "kernel32" stdcallcc fn ReadFile( |
| 175 | in_out_lpOverlapped: ?*OVERLAPPED, | 147 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 176 | ) BOOL; | 148 | ) BOOL; |
| 177 | 149 | ||
| 178 | pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; | 150 | pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL; |
| 151 | |||
| 152 | pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL; | ||
| 179 | 153 | ||
| 180 | pub extern "kernel32" stdcallcc fn SetFilePointerEx( | 154 | pub extern "kernel32" stdcallcc fn SetFilePointerEx( |
| 181 | in_fFile: HANDLE, | 155 | in_fFile: HANDLE, |
| ... | @@ -202,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile( | ... | @@ -202,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile( |
| 202 | 176 | ||
| 203 | pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL; | 177 | pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL; |
| 204 | 178 | ||
| 205 | //TODO: call unicode versions instead of relying on ANSI code page | 179 | pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE; |
| 206 | pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; | ||
| 207 | 180 | ||
| 208 | pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; | 181 | pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; |
| 209 | 182 | ||
| ... | @@ -232,3 +205,17 @@ pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; | ... | @@ -232,3 +205,17 @@ pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16; |
| 232 | pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; | 205 | pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2; |
| 233 | pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; | 206 | pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1; |
| 234 | pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; | 207 | pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; |
| 208 | |||
| 209 | |||
| 210 | pub const CONSOLE_SCREEN_BUFFER_INFO = extern struct { | ||
| 211 | dwSize: COORD, | ||
| 212 | dwCursorPosition: COORD, | ||
| 213 | wAttributes: WORD, | ||
| 214 | srWindow: SMALL_RECT, | ||
| 215 | dwMaximumWindowSize: COORD, | ||
| 216 | }; | ||
| 217 | |||
| 218 | pub const FOREGROUND_BLUE = 1; | ||
| 219 | pub const FOREGROUND_GREEN = 2; | ||
| 220 | pub const FOREGROUND_RED = 4; | ||
| 221 | pub const FOREGROUND_INTENSITY = 8; |
std/os/windows/ntdll.zig created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD; | ||
std/os/windows/ole32.zig-1| ... | @@ -5,7 +5,6 @@ pub extern "ole32.dll" stdcallcc fn CoUninitialize() void; | ... | @@ -5,7 +5,6 @@ pub extern "ole32.dll" stdcallcc fn CoUninitialize() void; |
| 5 | pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD; | 5 | pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD; |
| 6 | pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; | 6 | pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT; |
| 7 | 7 | ||
| 8 | |||
| 9 | pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; | 8 | pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED; |
| 10 | pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; | 9 | pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED; |
| 11 | pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; | 10 | pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE; |
std/os/windows/shlwapi.zig deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; | ||
| 4 | |||
std/os/windows/user32.zig deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; | ||
| 4 | |||
std/os/windows/util.zig+60-43| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../index.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const os = std.os; | 3 | const os = std.os; |
| 4 | const unicode = std.unicode; | ||
| 4 | const windows = std.os.windows; | 5 | const windows = std.os.windows; |
| 5 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| ... | @@ -118,16 +119,14 @@ pub const OpenError = error{ | ... | @@ -118,16 +119,14 @@ pub const OpenError = error{ |
| 118 | Unexpected, | 119 | Unexpected, |
| 119 | }; | 120 | }; |
| 120 | 121 | ||
| 121 | pub fn windowsOpen( | 122 | pub fn windowsOpenW( |
| 122 | file_path: []const u8, | 123 | file_path_w: [*]const u16, |
| 123 | desired_access: windows.DWORD, | 124 | desired_access: windows.DWORD, |
| 124 | share_mode: windows.DWORD, | 125 | share_mode: windows.DWORD, |
| 125 | creation_disposition: windows.DWORD, | 126 | creation_disposition: windows.DWORD, |
| 126 | flags_and_attrs: windows.DWORD, | 127 | flags_and_attrs: windows.DWORD, |
| 127 | ) OpenError!windows.HANDLE { | 128 | ) OpenError!windows.HANDLE { |
| 128 | const file_path_w = try sliceToPrefixedFileW(file_path); | 129 | const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); |
| 129 | |||
| 130 | const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); | ||
| 131 | 130 | ||
| 132 | if (result == windows.INVALID_HANDLE_VALUE) { | 131 | if (result == windows.INVALID_HANDLE_VALUE) { |
| 133 | const err = windows.GetLastError(); | 132 | const err = windows.GetLastError(); |
| ... | @@ -146,42 +145,63 @@ pub fn windowsOpen( | ... | @@ -146,42 +145,63 @@ pub fn windowsOpen( |
| 146 | return result; | 145 | return result; |
| 147 | } | 146 | } |
| 148 | 147 | ||
| 148 | pub fn windowsOpen( | ||
| 149 | file_path: []const u8, | ||
| 150 | desired_access: windows.DWORD, | ||
| 151 | share_mode: windows.DWORD, | ||
| 152 | creation_disposition: windows.DWORD, | ||
| 153 | flags_and_attrs: windows.DWORD, | ||
| 154 | ) OpenError!windows.HANDLE { | ||
| 155 | const file_path_w = try sliceToPrefixedFileW(file_path); | ||
| 156 | return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); | ||
| 157 | } | ||
| 158 | |||
| 149 | /// Caller must free result. | 159 | /// Caller must free result. |
| 150 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 { | 160 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { |
| 151 | // count bytes needed | 161 | // count bytes needed |
| 152 | const bytes_needed = x: { | 162 | const max_chars_needed = x: { |
| 153 | var bytes_needed: usize = 1; // 1 for the final null byte | 163 | var max_chars_needed: usize = 1; // 1 for the final null byte |
| 154 | var it = env_map.iterator(); | 164 | var it = env_map.iterator(); |
| 155 | while (it.next()) |pair| { | 165 | while (it.next()) |pair| { |
| 156 | // +1 for '=' | 166 | // +1 for '=' |
| 157 | // +1 for null byte | 167 | // +1 for null byte |
| 158 | bytes_needed += pair.key.len + pair.value.len + 2; | 168 | max_chars_needed += pair.key.len + pair.value.len + 2; |
| 159 | } | 169 | } |
| 160 | break :x bytes_needed; | 170 | break :x max_chars_needed; |
| 161 | }; | 171 | }; |
| 162 | const result = try allocator.alloc(u8, bytes_needed); | 172 | const result = try allocator.alloc(u16, max_chars_needed); |
| 163 | errdefer allocator.free(result); | 173 | errdefer allocator.free(result); |
| 164 | 174 | ||
| 165 | var it = env_map.iterator(); | 175 | var it = env_map.iterator(); |
| 166 | var i: usize = 0; | 176 | var i: usize = 0; |
| 167 | while (it.next()) |pair| { | 177 | while (it.next()) |pair| { |
| 168 | mem.copy(u8, result[i..], pair.key); | 178 | i += try unicode.utf8ToUtf16Le(result[i..], pair.key); |
| 169 | i += pair.key.len; | ||
| 170 | result[i] = '='; | 179 | result[i] = '='; |
| 171 | i += 1; | 180 | i += 1; |
| 172 | mem.copy(u8, result[i..], pair.value); | 181 | i += try unicode.utf8ToUtf16Le(result[i..], pair.value); |
| 173 | i += pair.value.len; | ||
| 174 | result[i] = 0; | 182 | result[i] = 0; |
| 175 | i += 1; | 183 | i += 1; |
| 176 | } | 184 | } |
| 177 | result[i] = 0; | 185 | result[i] = 0; |
| 178 | return result; | 186 | i += 1; |
| 187 | return allocator.shrink(u16, result, i); | ||
| 179 | } | 188 | } |
| 180 | 189 | ||
| 181 | pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE { | 190 | pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE { |
| 182 | const padded_buff = try cstr.addNullByte(allocator, dll_path); | 191 | return windows.LoadLibraryW(dll_path_w) orelse { |
| 183 | defer allocator.free(padded_buff); | 192 | const err = windows.GetLastError(); |
| 184 | return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound; | 193 | switch (err) { |
| 194 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | ||
| 195 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 196 | windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound, | ||
| 197 | else => return os.unexpectedErrorWindows(err), | ||
| 198 | } | ||
| 199 | }; | ||
| 200 | } | ||
| 201 | |||
| 202 | pub fn windowsLoadDll(dll_path: []const u8) !windows.HMODULE { | ||
| 203 | const dll_path_w = try sliceToPrefixedFileW(dll_path); | ||
| 204 | return windowsLoadDllW(&dll_path_w); | ||
| 185 | } | 205 | } |
| 186 | 206 | ||
| 187 | pub fn windowsUnloadDll(hModule: windows.HMODULE) void { | 207 | pub fn windowsUnloadDll(hModule: windows.HMODULE) void { |
| ... | @@ -191,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void { | ... | @@ -191,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void { |
| 191 | test "InvalidDll" { | 211 | test "InvalidDll" { |
| 192 | if (builtin.os != builtin.Os.windows) return error.SkipZigTest; | 212 | if (builtin.os != builtin.Os.windows) return error.SkipZigTest; |
| 193 | 213 | ||
| 194 | const DllName = "asdf.dll"; | 214 | const handle = os.windowsLoadDll("asdf.dll") catch |err| { |
| 195 | const allocator = std.debug.global_allocator; | 215 | assert(err == error.FileNotFound); |
| 196 | const handle = os.windowsLoadDll(allocator, DllName) catch |err| { | ||
| 197 | assert(err == error.DllNotFound); | ||
| 198 | return; | 216 | return; |
| 199 | }; | 217 | }; |
| 218 | @panic("Expected error from function"); | ||
| 200 | } | 219 | } |
| 201 | 220 | ||
| 202 | pub fn windowsFindFirstFile( | 221 | pub fn windowsFindFirstFile( |
| 203 | allocator: *mem.Allocator, | ||
| 204 | dir_path: []const u8, | 222 | dir_path: []const u8, |
| 205 | find_file_data: *windows.WIN32_FIND_DATAA, | 223 | find_file_data: *windows.WIN32_FIND_DATAW, |
| 206 | ) !windows.HANDLE { | 224 | ) !windows.HANDLE { |
| 207 | const wild_and_null = []u8{ '\\', '*', 0 }; | 225 | const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0}); |
| 208 | const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len); | 226 | const handle = windows.FindFirstFileW(&dir_path_w, find_file_data); |
| 209 | defer allocator.free(path_with_wild_and_null); | ||
| 210 | |||
| 211 | mem.copy(u8, path_with_wild_and_null, dir_path); | ||
| 212 | mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null); | ||
| 213 | |||
| 214 | const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data); | ||
| 215 | 227 | ||
| 216 | if (handle == windows.INVALID_HANDLE_VALUE) { | 228 | if (handle == windows.INVALID_HANDLE_VALUE) { |
| 217 | const err = windows.GetLastError(); | 229 | const err = windows.GetLastError(); |
| ... | @@ -226,8 +238,8 @@ pub fn windowsFindFirstFile( | ... | @@ -226,8 +238,8 @@ pub fn windowsFindFirstFile( |
| 226 | } | 238 | } |
| 227 | 239 | ||
| 228 | /// Returns `true` if there was another file, `false` otherwise. | 240 | /// Returns `true` if there was another file, `false` otherwise. |
| 229 | pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool { | 241 | pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool { |
| 230 | if (windows.FindNextFileA(handle, find_file_data) == 0) { | 242 | if (windows.FindNextFileW(handle, find_file_data) == 0) { |
| 231 | const err = windows.GetLastError(); | 243 | const err = windows.GetLastError(); |
| 232 | return switch (err) { | 244 | return switch (err) { |
| 233 | windows.ERROR.NO_MORE_FILES => false, | 245 | windows.ERROR.NO_MORE_FILES => false, |
| ... | @@ -288,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { | ... | @@ -288,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { |
| 288 | } | 300 | } |
| 289 | 301 | ||
| 290 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { | 302 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { |
| 303 | return sliceToPrefixedSuffixedFileW(s, []u16{0}); | ||
| 304 | } | ||
| 305 | |||
| 306 | pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { | ||
| 291 | // TODO well defined copy elision | 307 | // TODO well defined copy elision |
| 292 | var result: [PATH_MAX_WIDE + 1]u16 = undefined; | 308 | var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; |
| 293 | 309 | ||
| 294 | // > File I/O functions in the Windows API convert "/" to "\" as part of | 310 | // > File I/O functions in the Windows API convert "/" to "\" as part of |
| 295 | // > converting the name to an NT-style name, except when using the "\\?\" | 311 | // > converting the name to an NT-style name, except when using the "\\?\" |
| ... | @@ -297,11 +313,12 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { | ... | @@ -297,11 +313,12 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { |
| 297 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | 313 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation |
| 298 | // Because we want the larger maximum path length for absolute paths, we | 314 | // Because we want the larger maximum path length for absolute paths, we |
| 299 | // disallow forward slashes in zig std lib file functions on Windows. | 315 | // disallow forward slashes in zig std lib file functions on Windows. |
| 300 | for (s) |byte| | 316 | for (s) |byte| { |
| 301 | switch (byte) { | 317 | switch (byte) { |
| 302 | '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, | 318 | '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, |
| 303 | else => {}, | 319 | else => {}, |
| 304 | }; | 320 | } |
| 321 | } | ||
| 305 | const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { | 322 | const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { |
| 306 | const prefix = []u16{ '\\', '\\', '?', '\\' }; | 323 | const prefix = []u16{ '\\', '\\', '?', '\\' }; |
| 307 | mem.copy(u16, result[0..], prefix); | 324 | mem.copy(u16, result[0..], prefix); |
| ... | @@ -309,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { | ... | @@ -309,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { |
| 309 | }; | 326 | }; |
| 310 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); | 327 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); |
| 311 | assert(end_index <= result.len); | 328 | assert(end_index <= result.len); |
| 312 | if (end_index == result.len) return error.NameTooLong; | 329 | if (end_index + suffix.len > result.len) return error.NameTooLong; |
| 313 | result[end_index] = 0; | 330 | mem.copy(u16, result[end_index..], suffix); |
| 314 | return result; | 331 | return result; |
| 315 | } | 332 | } |
std/os/zen.zig+24-25| ... | @@ -6,32 +6,32 @@ const assert = std.debug.assert; | ... | @@ -6,32 +6,32 @@ const assert = std.debug.assert; |
| 6 | ////////////////////////// | 6 | ////////////////////////// |
| 7 | 7 | ||
| 8 | pub const Message = struct { | 8 | pub const Message = struct { |
| 9 | sender: MailboxId, | 9 | sender: MailboxId, |
| 10 | receiver: MailboxId, | 10 | receiver: MailboxId, |
| 11 | code: usize, | 11 | code: usize, |
| 12 | args: [5]usize, | 12 | args: [5]usize, |
| 13 | payload: ?[]const u8, | 13 | payload: ?[]const u8, |
| 14 | 14 | ||
| 15 | pub fn from(mailbox_id: *const MailboxId) Message { | 15 | pub fn from(mailbox_id: *const MailboxId) Message { |
| 16 | return Message { | 16 | return Message{ |
| 17 | .sender = MailboxId.Undefined, | 17 | .sender = MailboxId.Undefined, |
| 18 | .receiver = mailbox_id.*, | 18 | .receiver = mailbox_id.*, |
| 19 | .code = undefined, | 19 | .code = undefined, |
| 20 | .args = undefined, | 20 | .args = undefined, |
| 21 | .payload = null, | 21 | .payload = null, |
| 22 | }; | 22 | }; |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | pub fn to(mailbox_id: *const MailboxId, msg_code: usize, args: ...) Message { | 25 | pub fn to(mailbox_id: *const MailboxId, msg_code: usize, args: ...) Message { |
| 26 | var message = Message { | 26 | var message = Message{ |
| 27 | .sender = MailboxId.This, | 27 | .sender = MailboxId.This, |
| 28 | .receiver = mailbox_id.*, | 28 | .receiver = mailbox_id.*, |
| 29 | .code = msg_code, | 29 | .code = msg_code, |
| 30 | .args = undefined, | 30 | .args = undefined, |
| 31 | .payload = null, | 31 | .payload = null, |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | assert (args.len <= message.args.len); | 34 | assert(args.len <= message.args.len); |
| 35 | comptime var i = 0; | 35 | comptime var i = 0; |
| 36 | inline while (i < args.len) : (i += 1) { | 36 | inline while (i < args.len) : (i += 1) { |
| 37 | message.args[i] = args[i]; | 37 | message.args[i] = args[i]; |
| ... | @@ -111,8 +111,7 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize { | ... | @@ -111,8 +111,7 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize { |
| 111 | pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { | 111 | pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { |
| 112 | switch (fd) { | 112 | switch (fd) { |
| 113 | STDOUT_FILENO, STDERR_FILENO => { | 113 | STDOUT_FILENO, STDERR_FILENO => { |
| 114 | send(Message.to(Server.Terminal, 1) | 114 | send(Message.to(Server.Terminal, 1).withPayload(buf[0..count])); |
| 115 | .withPayload(buf[0..count])); | ||
| 116 | }, | 115 | }, |
| 117 | else => unreachable, | 116 | else => unreachable, |
| 118 | } | 117 | } |
| ... | @@ -124,14 +123,14 @@ pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { | ... | @@ -124,14 +123,14 @@ pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { |
| 124 | /////////////////////////// | 123 | /////////////////////////// |
| 125 | 124 | ||
| 126 | pub const Syscall = enum(usize) { | 125 | pub const Syscall = enum(usize) { |
| 127 | exit = 0, | 126 | exit = 0, |
| 128 | send = 1, | 127 | send = 1, |
| 129 | receive = 2, | 128 | receive = 2, |
| 130 | subscribeIRQ = 3, | 129 | subscribeIRQ = 3, |
| 131 | inb = 4, | 130 | inb = 4, |
| 132 | outb = 5, | 131 | outb = 5, |
| 133 | map = 6, | 132 | map = 6, |
| 134 | createThread = 7, | 133 | createThread = 7, |
| 135 | }; | 134 | }; |
| 136 | 135 | ||
| 137 | //////////////////// | 136 | //////////////////// |
std/pdb.zig created+646| ... | @@ -0,0 +1,646 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("index.zig"); | ||
| 3 | const io = std.io; | ||
| 4 | const math = std.math; | ||
| 5 | const mem = std.mem; | ||
| 6 | const os = std.os; | ||
| 7 | const warn = std.debug.warn; | ||
| 8 | const coff = std.coff; | ||
| 9 | |||
| 10 | const ArrayList = std.ArrayList; | ||
| 11 | |||
| 12 | // https://llvm.org/docs/PDB/DbiStream.html#stream-header | ||
| 13 | pub const DbiStreamHeader = packed struct { | ||
| 14 | VersionSignature: i32, | ||
| 15 | VersionHeader: u32, | ||
| 16 | Age: u32, | ||
| 17 | GlobalStreamIndex: u16, | ||
| 18 | BuildNumber: u16, | ||
| 19 | PublicStreamIndex: u16, | ||
| 20 | PdbDllVersion: u16, | ||
| 21 | SymRecordStream: u16, | ||
| 22 | PdbDllRbld: u16, | ||
| 23 | ModInfoSize: u32, | ||
| 24 | SectionContributionSize: u32, | ||
| 25 | SectionMapSize: u32, | ||
| 26 | SourceInfoSize: i32, | ||
| 27 | TypeServerSize: i32, | ||
| 28 | MFCTypeServerIndex: u32, | ||
| 29 | OptionalDbgHeaderSize: i32, | ||
| 30 | ECSubstreamSize: i32, | ||
| 31 | Flags: u16, | ||
| 32 | Machine: u16, | ||
| 33 | Padding: u32, | ||
| 34 | }; | ||
| 35 | |||
| 36 | pub const SectionContribEntry = packed struct { | ||
| 37 | Section: u16, | ||
| 38 | Padding1: [2]u8, | ||
| 39 | Offset: u32, | ||
| 40 | Size: u32, | ||
| 41 | Characteristics: u32, | ||
| 42 | ModuleIndex: u16, | ||
| 43 | Padding2: [2]u8, | ||
| 44 | DataCrc: u32, | ||
| 45 | RelocCrc: u32, | ||
| 46 | }; | ||
| 47 | |||
| 48 | pub const ModInfo = packed struct { | ||
| 49 | Unused1: u32, | ||
| 50 | SectionContr: SectionContribEntry, | ||
| 51 | Flags: u16, | ||
| 52 | ModuleSymStream: u16, | ||
| 53 | SymByteSize: u32, | ||
| 54 | C11ByteSize: u32, | ||
| 55 | C13ByteSize: u32, | ||
| 56 | SourceFileCount: u16, | ||
| 57 | Padding: [2]u8, | ||
| 58 | Unused2: u32, | ||
| 59 | SourceFileNameIndex: u32, | ||
| 60 | PdbFilePathNameIndex: u32, | ||
| 61 | // These fields are variable length | ||
| 62 | //ModuleName: char[], | ||
| 63 | //ObjFileName: char[], | ||
| 64 | }; | ||
| 65 | |||
| 66 | pub const SectionMapHeader = packed struct { | ||
| 67 | Count: u16, /// Number of segment descriptors | ||
| 68 | LogCount: u16, /// Number of logical segment descriptors | ||
| 69 | }; | ||
| 70 | |||
| 71 | pub const SectionMapEntry = packed struct { | ||
| 72 | Flags: u16 , /// See the SectionMapEntryFlags enum below. | ||
| 73 | Ovl: u16 , /// Logical overlay number | ||
| 74 | Group: u16 , /// Group index into descriptor array. | ||
| 75 | Frame: u16 , | ||
| 76 | SectionName: u16 , /// Byte index of segment / group name in string table, or 0xFFFF. | ||
| 77 | ClassName: u16 , /// Byte index of class in string table, or 0xFFFF. | ||
| 78 | Offset: u32 , /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group. | ||
| 79 | SectionLength: u32 , /// Byte count of the segment or group. | ||
| 80 | }; | ||
| 81 | |||
| 82 | pub const StreamType = enum(u16) { | ||
| 83 | Pdb = 1, | ||
| 84 | Tpi = 2, | ||
| 85 | Dbi = 3, | ||
| 86 | Ipi = 4, | ||
| 87 | }; | ||
| 88 | |||
| 89 | /// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful | ||
| 90 | /// for reference purposes and when dealing with unknown record types. | ||
| 91 | pub const SymbolKind = packed enum(u16) { | ||
| 92 | S_COMPILE = 1, | ||
| 93 | S_REGISTER_16t = 2, | ||
| 94 | S_CONSTANT_16t = 3, | ||
| 95 | S_UDT_16t = 4, | ||
| 96 | S_SSEARCH = 5, | ||
| 97 | S_SKIP = 7, | ||
| 98 | S_CVRESERVE = 8, | ||
| 99 | S_OBJNAME_ST = 9, | ||
| 100 | S_ENDARG = 10, | ||
| 101 | S_COBOLUDT_16t = 11, | ||
| 102 | S_MANYREG_16t = 12, | ||
| 103 | S_RETURN = 13, | ||
| 104 | S_ENTRYTHIS = 14, | ||
| 105 | S_BPREL16 = 256, | ||
| 106 | S_LDATA16 = 257, | ||
| 107 | S_GDATA16 = 258, | ||
| 108 | S_PUB16 = 259, | ||
| 109 | S_LPROC16 = 260, | ||
| 110 | S_GPROC16 = 261, | ||
| 111 | S_THUNK16 = 262, | ||
| 112 | S_BLOCK16 = 263, | ||
| 113 | S_WITH16 = 264, | ||
| 114 | S_LABEL16 = 265, | ||
| 115 | S_CEXMODEL16 = 266, | ||
| 116 | S_VFTABLE16 = 267, | ||
| 117 | S_REGREL16 = 268, | ||
| 118 | S_BPREL32_16t = 512, | ||
| 119 | S_LDATA32_16t = 513, | ||
| 120 | S_GDATA32_16t = 514, | ||
| 121 | S_PUB32_16t = 515, | ||
| 122 | S_LPROC32_16t = 516, | ||
| 123 | S_GPROC32_16t = 517, | ||
| 124 | S_THUNK32_ST = 518, | ||
| 125 | S_BLOCK32_ST = 519, | ||
| 126 | S_WITH32_ST = 520, | ||
| 127 | S_LABEL32_ST = 521, | ||
| 128 | S_CEXMODEL32 = 522, | ||
| 129 | S_VFTABLE32_16t = 523, | ||
| 130 | S_REGREL32_16t = 524, | ||
| 131 | S_LTHREAD32_16t = 525, | ||
| 132 | S_GTHREAD32_16t = 526, | ||
| 133 | S_SLINK32 = 527, | ||
| 134 | S_LPROCMIPS_16t = 768, | ||
| 135 | S_GPROCMIPS_16t = 769, | ||
| 136 | S_PROCREF_ST = 1024, | ||
| 137 | S_DATAREF_ST = 1025, | ||
| 138 | S_ALIGN = 1026, | ||
| 139 | S_LPROCREF_ST = 1027, | ||
| 140 | S_OEM = 1028, | ||
| 141 | S_TI16_MAX = 4096, | ||
| 142 | S_REGISTER_ST = 4097, | ||
| 143 | S_CONSTANT_ST = 4098, | ||
| 144 | S_UDT_ST = 4099, | ||
| 145 | S_COBOLUDT_ST = 4100, | ||
| 146 | S_MANYREG_ST = 4101, | ||
| 147 | S_BPREL32_ST = 4102, | ||
| 148 | S_LDATA32_ST = 4103, | ||
| 149 | S_GDATA32_ST = 4104, | ||
| 150 | S_PUB32_ST = 4105, | ||
| 151 | S_LPROC32_ST = 4106, | ||
| 152 | S_GPROC32_ST = 4107, | ||
| 153 | S_VFTABLE32 = 4108, | ||
| 154 | S_REGREL32_ST = 4109, | ||
| 155 | S_LTHREAD32_ST = 4110, | ||
| 156 | S_GTHREAD32_ST = 4111, | ||
| 157 | S_LPROCMIPS_ST = 4112, | ||
| 158 | S_GPROCMIPS_ST = 4113, | ||
| 159 | S_COMPILE2_ST = 4115, | ||
| 160 | S_MANYREG2_ST = 4116, | ||
| 161 | S_LPROCIA64_ST = 4117, | ||
| 162 | S_GPROCIA64_ST = 4118, | ||
| 163 | S_LOCALSLOT_ST = 4119, | ||
| 164 | S_PARAMSLOT_ST = 4120, | ||
| 165 | S_ANNOTATION = 4121, | ||
| 166 | S_GMANPROC_ST = 4122, | ||
| 167 | S_LMANPROC_ST = 4123, | ||
| 168 | S_RESERVED1 = 4124, | ||
| 169 | S_RESERVED2 = 4125, | ||
| 170 | S_RESERVED3 = 4126, | ||
| 171 | S_RESERVED4 = 4127, | ||
| 172 | S_LMANDATA_ST = 4128, | ||
| 173 | S_GMANDATA_ST = 4129, | ||
| 174 | S_MANFRAMEREL_ST = 4130, | ||
| 175 | S_MANREGISTER_ST = 4131, | ||
| 176 | S_MANSLOT_ST = 4132, | ||
| 177 | S_MANMANYREG_ST = 4133, | ||
| 178 | S_MANREGREL_ST = 4134, | ||
| 179 | S_MANMANYREG2_ST = 4135, | ||
| 180 | S_MANTYPREF = 4136, | ||
| 181 | S_UNAMESPACE_ST = 4137, | ||
| 182 | S_ST_MAX = 4352, | ||
| 183 | S_WITH32 = 4356, | ||
| 184 | S_MANYREG = 4362, | ||
| 185 | S_LPROCMIPS = 4372, | ||
| 186 | S_GPROCMIPS = 4373, | ||
| 187 | S_MANYREG2 = 4375, | ||
| 188 | S_LPROCIA64 = 4376, | ||
| 189 | S_GPROCIA64 = 4377, | ||
| 190 | S_LOCALSLOT = 4378, | ||
| 191 | S_PARAMSLOT = 4379, | ||
| 192 | S_MANFRAMEREL = 4382, | ||
| 193 | S_MANREGISTER = 4383, | ||
| 194 | S_MANSLOT = 4384, | ||
| 195 | S_MANMANYREG = 4385, | ||
| 196 | S_MANREGREL = 4386, | ||
| 197 | S_MANMANYREG2 = 4387, | ||
| 198 | S_UNAMESPACE = 4388, | ||
| 199 | S_DATAREF = 4390, | ||
| 200 | S_ANNOTATIONREF = 4392, | ||
| 201 | S_TOKENREF = 4393, | ||
| 202 | S_GMANPROC = 4394, | ||
| 203 | S_LMANPROC = 4395, | ||
| 204 | S_ATTR_FRAMEREL = 4398, | ||
| 205 | S_ATTR_REGISTER = 4399, | ||
| 206 | S_ATTR_REGREL = 4400, | ||
| 207 | S_ATTR_MANYREG = 4401, | ||
| 208 | S_SEPCODE = 4402, | ||
| 209 | S_LOCAL_2005 = 4403, | ||
| 210 | S_DEFRANGE_2005 = 4404, | ||
| 211 | S_DEFRANGE2_2005 = 4405, | ||
| 212 | S_DISCARDED = 4411, | ||
| 213 | S_LPROCMIPS_ID = 4424, | ||
| 214 | S_GPROCMIPS_ID = 4425, | ||
| 215 | S_LPROCIA64_ID = 4426, | ||
| 216 | S_GPROCIA64_ID = 4427, | ||
| 217 | S_DEFRANGE_HLSL = 4432, | ||
| 218 | S_GDATA_HLSL = 4433, | ||
| 219 | S_LDATA_HLSL = 4434, | ||
| 220 | S_LOCAL_DPC_GROUPSHARED = 4436, | ||
| 221 | S_DEFRANGE_DPC_PTR_TAG = 4439, | ||
| 222 | S_DPC_SYM_TAG_MAP = 4440, | ||
| 223 | S_ARMSWITCHTABLE = 4441, | ||
| 224 | S_POGODATA = 4444, | ||
| 225 | S_INLINESITE2 = 4445, | ||
| 226 | S_MOD_TYPEREF = 4447, | ||
| 227 | S_REF_MINIPDB = 4448, | ||
| 228 | S_PDBMAP = 4449, | ||
| 229 | S_GDATA_HLSL32 = 4450, | ||
| 230 | S_LDATA_HLSL32 = 4451, | ||
| 231 | S_GDATA_HLSL32_EX = 4452, | ||
| 232 | S_LDATA_HLSL32_EX = 4453, | ||
| 233 | S_FASTLINK = 4455, | ||
| 234 | S_INLINEES = 4456, | ||
| 235 | S_END = 6, | ||
| 236 | S_INLINESITE_END = 4430, | ||
| 237 | S_PROC_ID_END = 4431, | ||
| 238 | S_THUNK32 = 4354, | ||
| 239 | S_TRAMPOLINE = 4396, | ||
| 240 | S_SECTION = 4406, | ||
| 241 | S_COFFGROUP = 4407, | ||
| 242 | S_EXPORT = 4408, | ||
| 243 | S_LPROC32 = 4367, | ||
| 244 | S_GPROC32 = 4368, | ||
| 245 | S_LPROC32_ID = 4422, | ||
| 246 | S_GPROC32_ID = 4423, | ||
| 247 | S_LPROC32_DPC = 4437, | ||
| 248 | S_LPROC32_DPC_ID = 4438, | ||
| 249 | S_REGISTER = 4358, | ||
| 250 | S_PUB32 = 4366, | ||
| 251 | S_PROCREF = 4389, | ||
| 252 | S_LPROCREF = 4391, | ||
| 253 | S_ENVBLOCK = 4413, | ||
| 254 | S_INLINESITE = 4429, | ||
| 255 | S_LOCAL = 4414, | ||
| 256 | S_DEFRANGE = 4415, | ||
| 257 | S_DEFRANGE_SUBFIELD = 4416, | ||
| 258 | S_DEFRANGE_REGISTER = 4417, | ||
| 259 | S_DEFRANGE_FRAMEPOINTER_REL = 4418, | ||
| 260 | S_DEFRANGE_SUBFIELD_REGISTER = 4419, | ||
| 261 | S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 4420, | ||
| 262 | S_DEFRANGE_REGISTER_REL = 4421, | ||
| 263 | S_BLOCK32 = 4355, | ||
| 264 | S_LABEL32 = 4357, | ||
| 265 | S_OBJNAME = 4353, | ||
| 266 | S_COMPILE2 = 4374, | ||
| 267 | S_COMPILE3 = 4412, | ||
| 268 | S_FRAMEPROC = 4114, | ||
| 269 | S_CALLSITEINFO = 4409, | ||
| 270 | S_FILESTATIC = 4435, | ||
| 271 | S_HEAPALLOCSITE = 4446, | ||
| 272 | S_FRAMECOOKIE = 4410, | ||
| 273 | S_CALLEES = 4442, | ||
| 274 | S_CALLERS = 4443, | ||
| 275 | S_UDT = 4360, | ||
| 276 | S_COBOLUDT = 4361, | ||
| 277 | S_BUILDINFO = 4428, | ||
| 278 | S_BPREL32 = 4363, | ||
| 279 | S_REGREL32 = 4369, | ||
| 280 | S_CONSTANT = 4359, | ||
| 281 | S_MANCONSTANT = 4397, | ||
| 282 | S_LDATA32 = 4364, | ||
| 283 | S_GDATA32 = 4365, | ||
| 284 | S_LMANDATA = 4380, | ||
| 285 | S_GMANDATA = 4381, | ||
| 286 | S_LTHREAD32 = 4370, | ||
| 287 | S_GTHREAD32 = 4371, | ||
| 288 | }; | ||
| 289 | |||
| 290 | pub const TypeIndex = u32; | ||
| 291 | |||
| 292 | pub const ProcSym = packed struct { | ||
| 293 | Parent: u32 , | ||
| 294 | End: u32 , | ||
| 295 | Next: u32 , | ||
| 296 | CodeSize: u32 , | ||
| 297 | DbgStart: u32 , | ||
| 298 | DbgEnd: u32 , | ||
| 299 | FunctionType: TypeIndex , | ||
| 300 | CodeOffset: u32, | ||
| 301 | Segment: u16, | ||
| 302 | Flags: ProcSymFlags, | ||
| 303 | // following is a null terminated string | ||
| 304 | // Name: [*]u8, | ||
| 305 | }; | ||
| 306 | |||
| 307 | pub const ProcSymFlags = packed struct { | ||
| 308 | HasFP: bool, | ||
| 309 | HasIRET: bool, | ||
| 310 | HasFRET: bool, | ||
| 311 | IsNoReturn: bool, | ||
| 312 | IsUnreachable: bool, | ||
| 313 | HasCustomCallingConv: bool, | ||
| 314 | IsNoInline: bool, | ||
| 315 | HasOptimizedDebugInfo: bool, | ||
| 316 | }; | ||
| 317 | |||
| 318 | pub const SectionContrSubstreamVersion = enum(u32) { | ||
| 319 | Ver60 = 0xeffe0000 + 19970605, | ||
| 320 | V2 = 0xeffe0000 + 20140516 | ||
| 321 | }; | ||
| 322 | |||
| 323 | pub const RecordPrefix = packed struct { | ||
| 324 | RecordLen: u16, /// Record length, starting from &RecordKind. | ||
| 325 | RecordKind: SymbolKind, /// Record kind enum (SymRecordKind or TypeRecordKind) | ||
| 326 | }; | ||
| 327 | |||
| 328 | pub const LineFragmentHeader = packed struct { | ||
| 329 | RelocOffset: u32, /// Code offset of line contribution. | ||
| 330 | RelocSegment: u16, /// Code segment of line contribution. | ||
| 331 | Flags: LineFlags, | ||
| 332 | CodeSize: u32, /// Code size of this line contribution. | ||
| 333 | }; | ||
| 334 | |||
| 335 | pub const LineFlags = packed struct { | ||
| 336 | LF_HaveColumns: bool, /// CV_LINES_HAVE_COLUMNS | ||
| 337 | unused: u15, | ||
| 338 | }; | ||
| 339 | |||
| 340 | /// The following two variable length arrays appear immediately after the | ||
| 341 | /// header. The structure definitions follow. | ||
| 342 | /// LineNumberEntry Lines[NumLines]; | ||
| 343 | /// ColumnNumberEntry Columns[NumLines]; | ||
| 344 | pub const LineBlockFragmentHeader = packed struct { | ||
| 345 | /// Offset of FileChecksum entry in File | ||
| 346 | /// checksums buffer. The checksum entry then | ||
| 347 | /// contains another offset into the string | ||
| 348 | /// table of the actual name. | ||
| 349 | NameIndex: u32, | ||
| 350 | NumLines: u32, | ||
| 351 | BlockSize: u32, /// code size of block, in bytes | ||
| 352 | }; | ||
| 353 | |||
| 354 | |||
| 355 | pub const LineNumberEntry = packed struct { | ||
| 356 | Offset: u32, /// Offset to start of code bytes for line number | ||
| 357 | Flags: u32, | ||
| 358 | |||
| 359 | /// TODO runtime crash when I make the actual type of Flags this | ||
| 360 | const Flags = packed struct { | ||
| 361 | Start: u24, | ||
| 362 | End: u7, | ||
| 363 | IsStatement: bool, | ||
| 364 | }; | ||
| 365 | }; | ||
| 366 | |||
| 367 | pub const ColumnNumberEntry = packed struct { | ||
| 368 | StartColumn: u16, | ||
| 369 | EndColumn: u16, | ||
| 370 | }; | ||
| 371 | |||
| 372 | /// Checksum bytes follow. | ||
| 373 | pub const FileChecksumEntryHeader = packed struct { | ||
| 374 | FileNameOffset: u32, /// Byte offset of filename in global string table. | ||
| 375 | ChecksumSize: u8, /// Number of bytes of checksum. | ||
| 376 | ChecksumKind: u8, /// FileChecksumKind | ||
| 377 | }; | ||
| 378 | |||
| 379 | pub const DebugSubsectionKind = packed enum(u32) { | ||
| 380 | None = 0, | ||
| 381 | Symbols = 0xf1, | ||
| 382 | Lines = 0xf2, | ||
| 383 | StringTable = 0xf3, | ||
| 384 | FileChecksums = 0xf4, | ||
| 385 | FrameData = 0xf5, | ||
| 386 | InlineeLines = 0xf6, | ||
| 387 | CrossScopeImports = 0xf7, | ||
| 388 | CrossScopeExports = 0xf8, | ||
| 389 | |||
| 390 | // These appear to relate to .Net assembly info. | ||
| 391 | ILLines = 0xf9, | ||
| 392 | FuncMDTokenMap = 0xfa, | ||
| 393 | TypeMDTokenMap = 0xfb, | ||
| 394 | MergedAssemblyInput = 0xfc, | ||
| 395 | |||
| 396 | CoffSymbolRVA = 0xfd, | ||
| 397 | }; | ||
| 398 | |||
| 399 | |||
| 400 | pub const DebugSubsectionHeader = packed struct { | ||
| 401 | Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum | ||
| 402 | Length: u32, /// number of bytes occupied by this record. | ||
| 403 | }; | ||
| 404 | |||
| 405 | |||
| 406 | pub const PDBStringTableHeader = packed struct { | ||
| 407 | Signature: u32, /// PDBStringTableSignature | ||
| 408 | HashVersion: u32, /// 1 or 2 | ||
| 409 | ByteSize: u32, /// Number of bytes of names buffer. | ||
| 410 | }; | ||
| 411 | |||
| 412 | pub const Pdb = struct { | ||
| 413 | in_file: os.File, | ||
| 414 | allocator: *mem.Allocator, | ||
| 415 | coff: *coff.Coff, | ||
| 416 | string_table: *MsfStream, | ||
| 417 | dbi: *MsfStream, | ||
| 418 | |||
| 419 | msf: Msf, | ||
| 420 | |||
| 421 | pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { | ||
| 422 | self.in_file = try os.File.openRead(file_name); | ||
| 423 | self.allocator = coff_ptr.allocator; | ||
| 424 | self.coff = coff_ptr; | ||
| 425 | |||
| 426 | try self.msf.openFile(self.allocator, self.in_file); | ||
| 427 | } | ||
| 428 | |||
| 429 | pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream { | ||
| 430 | if (id >= self.msf.streams.len) | ||
| 431 | return null; | ||
| 432 | return &self.msf.streams[id]; | ||
| 433 | } | ||
| 434 | |||
| 435 | pub fn getStream(self: *Pdb, stream: StreamType) ?*MsfStream { | ||
| 436 | const id = @enumToInt(stream); | ||
| 437 | return self.getStreamById(id); | ||
| 438 | } | ||
| 439 | }; | ||
| 440 | |||
| 441 | // see https://llvm.org/docs/PDB/MsfFile.html | ||
| 442 | const Msf = struct { | ||
| 443 | directory: MsfStream, | ||
| 444 | streams: []MsfStream, | ||
| 445 | |||
| 446 | fn openFile(self: *Msf, allocator: *mem.Allocator, file: os.File) !void { | ||
| 447 | var file_stream = io.FileInStream.init(file); | ||
| 448 | const in = &file_stream.stream; | ||
| 449 | |||
| 450 | var superblock: SuperBlock = undefined; | ||
| 451 | try in.readStruct(SuperBlock, &superblock); | ||
| 452 | |||
| 453 | if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic)) | ||
| 454 | return error.InvalidDebugInfo; | ||
| 455 | |||
| 456 | switch (superblock.BlockSize) { | ||
| 457 | // llvm only supports 4096 but we can handle any of these values | ||
| 458 | 512, 1024, 2048, 4096 => {}, | ||
| 459 | else => return error.InvalidDebugInfo | ||
| 460 | } | ||
| 461 | |||
| 462 | if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) | ||
| 463 | return error.InvalidDebugInfo; | ||
| 464 | |||
| 465 | self.directory = try MsfStream.init( | ||
| 466 | superblock.BlockSize, | ||
| 467 | blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize), | ||
| 468 | superblock.BlockSize * superblock.BlockMapAddr, | ||
| 469 | file, | ||
| 470 | allocator, | ||
| 471 | ); | ||
| 472 | |||
| 473 | const stream_count = try self.directory.stream.readIntLe(u32); | ||
| 474 | |||
| 475 | const stream_sizes = try allocator.alloc(u32, stream_count); | ||
| 476 | for (stream_sizes) |*s| { | ||
| 477 | const size = try self.directory.stream.readIntLe(u32); | ||
| 478 | s.* = blockCountFromSize(size, superblock.BlockSize); | ||
| 479 | } | ||
| 480 | |||
| 481 | self.streams = try allocator.alloc(MsfStream, stream_count); | ||
| 482 | for (self.streams) |*stream, i| { | ||
| 483 | stream.* = try MsfStream.init( | ||
| 484 | superblock.BlockSize, | ||
| 485 | stream_sizes[i], | ||
| 486 | // MsfStream.init expects the file to be at the part where it reads [N]u32 | ||
| 487 | try file.getPos(), | ||
| 488 | file, | ||
| 489 | allocator, | ||
| 490 | ); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | }; | ||
| 494 | |||
| 495 | fn blockCountFromSize(size: u32, block_size: u32) u32 { | ||
| 496 | return (size + block_size - 1) / block_size; | ||
| 497 | } | ||
| 498 | |||
| 499 | // https://llvm.org/docs/PDB/MsfFile.html#the-superblock | ||
| 500 | const SuperBlock = packed struct { | ||
| 501 | /// The LLVM docs list a space between C / C++ but empirically this is not the case. | ||
| 502 | const file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00"; | ||
| 503 | |||
| 504 | FileMagic: [file_magic.len]u8, | ||
| 505 | |||
| 506 | /// The block size of the internal file system. Valid values are 512, 1024, | ||
| 507 | /// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending | ||
| 508 | /// on the block sizes. For the purposes of LLVM, we handle only block sizes of | ||
| 509 | /// 4KiB, and all further discussion assumes a block size of 4KiB. | ||
| 510 | BlockSize: u32, | ||
| 511 | |||
| 512 | /// The index of a block within the file, at which begins a bitfield representing | ||
| 513 | /// the set of all blocks within the file which are “free” (i.e. the data within | ||
| 514 | /// that block is not used). See The Free Block Map for more information. Important: | ||
| 515 | /// FreeBlockMapBlock can only be 1 or 2! | ||
| 516 | FreeBlockMapBlock: u32, | ||
| 517 | |||
| 518 | /// The total number of blocks in the file. NumBlocks * BlockSize should equal the | ||
| 519 | /// size of the file on disk. | ||
| 520 | NumBlocks: u32, | ||
| 521 | |||
| 522 | /// The size of the stream directory, in bytes. The stream directory contains | ||
| 523 | /// information about each stream’s size and the set of blocks that it occupies. | ||
| 524 | /// It will be described in more detail later. | ||
| 525 | NumDirectoryBytes: u32, | ||
| 526 | |||
| 527 | Unknown: u32, | ||
| 528 | |||
| 529 | /// The index of a block within the MSF file. At this block is an array of | ||
| 530 | /// ulittle32_t’s listing the blocks that the stream directory resides on. | ||
| 531 | /// For large MSF files, the stream directory (which describes the block | ||
| 532 | /// layout of each stream) may not fit entirely on a single block. As a | ||
| 533 | /// result, this extra layer of indirection is introduced, whereby this | ||
| 534 | /// block contains the list of blocks that the stream directory occupies, | ||
| 535 | /// and the stream directory itself can be stitched together accordingly. | ||
| 536 | /// The number of ulittle32_t’s in this array is given by | ||
| 537 | /// ceil(NumDirectoryBytes / BlockSize). | ||
| 538 | BlockMapAddr: u32, | ||
| 539 | |||
| 540 | }; | ||
| 541 | |||
| 542 | const MsfStream = struct { | ||
| 543 | in_file: os.File, | ||
| 544 | pos: usize, | ||
| 545 | blocks: []u32, | ||
| 546 | block_size: u32, | ||
| 547 | |||
| 548 | /// Implementation of InStream trait for Pdb.MsfStream | ||
| 549 | stream: Stream, | ||
| 550 | |||
| 551 | pub const Error = @typeOf(read).ReturnType.ErrorSet; | ||
| 552 | pub const Stream = io.InStream(Error); | ||
| 553 | |||
| 554 | fn init(block_size: u32, block_count: u32, pos: usize, file: os.File, allocator: *mem.Allocator) !MsfStream { | ||
| 555 | var stream = MsfStream { | ||
| 556 | .in_file = file, | ||
| 557 | .pos = 0, | ||
| 558 | .blocks = try allocator.alloc(u32, block_count), | ||
| 559 | .block_size = block_size, | ||
| 560 | .stream = Stream { | ||
| 561 | .readFn = readFn, | ||
| 562 | }, | ||
| 563 | }; | ||
| 564 | |||
| 565 | var file_stream = io.FileInStream.init(file); | ||
| 566 | const in = &file_stream.stream; | ||
| 567 | try file.seekTo(pos); | ||
| 568 | |||
| 569 | var i: u32 = 0; | ||
| 570 | while (i < block_count) : (i += 1) { | ||
| 571 | stream.blocks[i] = try in.readIntLe(u32); | ||
| 572 | } | ||
| 573 | |||
| 574 | return stream; | ||
| 575 | } | ||
| 576 | |||
| 577 | fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 { | ||
| 578 | var list = ArrayList(u8).init(allocator); | ||
| 579 | defer list.deinit(); | ||
| 580 | while (true) { | ||
| 581 | const byte = try self.stream.readByte(); | ||
| 582 | if (byte == 0) { | ||
| 583 | return list.toSlice(); | ||
| 584 | } | ||
| 585 | try list.append(byte); | ||
| 586 | } | ||
| 587 | } | ||
| 588 | |||
| 589 | fn read(self: *MsfStream, buffer: []u8) !usize { | ||
| 590 | var block_id = self.pos / self.block_size; | ||
| 591 | var block = self.blocks[block_id]; | ||
| 592 | var offset = self.pos % self.block_size; | ||
| 593 | |||
| 594 | try self.in_file.seekTo(block * self.block_size + offset); | ||
| 595 | var file_stream = io.FileInStream.init(self.in_file); | ||
| 596 | const in = &file_stream.stream; | ||
| 597 | |||
| 598 | var size: usize = 0; | ||
| 599 | for (buffer) |*byte| { | ||
| 600 | byte.* = try in.readByte(); | ||
| 601 | |||
| 602 | offset += 1; | ||
| 603 | size += 1; | ||
| 604 | |||
| 605 | // If we're at the end of a block, go to the next one. | ||
| 606 | if (offset == self.block_size) { | ||
| 607 | offset = 0; | ||
| 608 | block_id += 1; | ||
| 609 | block = self.blocks[block_id]; | ||
| 610 | try self.in_file.seekTo(block * self.block_size); | ||
| 611 | } | ||
| 612 | } | ||
| 613 | |||
| 614 | self.pos += size; | ||
| 615 | return size; | ||
| 616 | } | ||
| 617 | |||
| 618 | fn seekForward(self: *MsfStream, len: usize) !void { | ||
| 619 | self.pos += len; | ||
| 620 | if (self.pos >= self.blocks.len * self.block_size) | ||
| 621 | return error.EOF; | ||
| 622 | } | ||
| 623 | |||
| 624 | fn seekTo(self: *MsfStream, len: usize) !void { | ||
| 625 | self.pos = len; | ||
| 626 | if (self.pos >= self.blocks.len * self.block_size) | ||
| 627 | return error.EOF; | ||
| 628 | } | ||
| 629 | |||
| 630 | fn getSize(self: *const MsfStream) usize { | ||
| 631 | return self.blocks.len * self.block_size; | ||
| 632 | } | ||
| 633 | |||
| 634 | fn getFilePos(self: MsfStream) usize { | ||
| 635 | const block_id = self.pos / self.block_size; | ||
| 636 | const block = self.blocks[block_id]; | ||
| 637 | const offset = self.pos % self.block_size; | ||
| 638 | |||
| 639 | return block * self.block_size + offset; | ||
| 640 | } | ||
| 641 | |||
| 642 | fn readFn(in_stream: *Stream, buffer: []u8) Error!usize { | ||
| 643 | const self = @fieldParentPtr(MsfStream, "stream", in_stream); | ||
| 644 | return self.read(buffer); | ||
| 645 | } | ||
| 646 | }; | ||
std/rb.zig+16-14| ... | @@ -9,9 +9,7 @@ const Color = enum(u1) { | ... | @@ -9,9 +9,7 @@ const Color = enum(u1) { |
| 9 | const Red = Color.Red; | 9 | const Red = Color.Red; |
| 10 | const Black = Color.Black; | 10 | const Black = Color.Black; |
| 11 | 11 | ||
| 12 | const ReplaceError = error { | 12 | const ReplaceError = error{NotEqual}; |
| 13 | NotEqual, | ||
| 14 | }; | ||
| 15 | 13 | ||
| 16 | /// Insert this into your struct that you want to add to a red-black tree. | 14 | /// Insert this into your struct that you want to add to a red-black tree. |
| 17 | /// Do not use a pointer. Turn the *rb.Node results of the functions in rb | 15 | /// Do not use a pointer. Turn the *rb.Node results of the functions in rb |
| ... | @@ -21,13 +19,15 @@ const ReplaceError = error { | ... | @@ -21,13 +19,15 @@ const ReplaceError = error { |
| 21 | /// node: rb.Node, | 19 | /// node: rb.Node, |
| 22 | /// value: i32, | 20 | /// value: i32, |
| 23 | /// }; | 21 | /// }; |
| 24 | /// fn number(node: *Node) Number { | 22 | /// fn number(node: *rb.Node) Number { |
| 25 | /// return @fieldParentPtr(Number, "node", node); | 23 | /// return @fieldParentPtr(Number, "node", node); |
| 26 | /// } | 24 | /// } |
| 27 | pub const Node = struct { | 25 | pub const Node = struct { |
| 28 | left: ?*Node, | 26 | left: ?*Node, |
| 29 | right: ?*Node, | 27 | right: ?*Node, |
| 30 | parent_and_color: usize, /// parent | color | 28 | |
| 29 | /// parent | color | ||
| 30 | parent_and_color: usize, | ||
| 31 | 31 | ||
| 32 | pub fn next(constnode: *Node) ?*Node { | 32 | pub fn next(constnode: *Node) ?*Node { |
| 33 | var node = constnode; | 33 | var node = constnode; |
| ... | @@ -130,7 +130,7 @@ pub const Node = struct { | ... | @@ -130,7 +130,7 @@ pub const Node = struct { |
| 130 | 130 | ||
| 131 | pub const Tree = struct { | 131 | pub const Tree = struct { |
| 132 | root: ?*Node, | 132 | root: ?*Node, |
| 133 | compareFn: fn(*Node, *Node) mem.Compare, | 133 | compareFn: fn (*Node, *Node) mem.Compare, |
| 134 | 134 | ||
| 135 | /// If you have a need for a version that caches this, please file a bug. | 135 | /// If you have a need for a version that caches this, please file a bug. |
| 136 | pub fn first(tree: *Tree) ?*Node { | 136 | pub fn first(tree: *Tree) ?*Node { |
| ... | @@ -180,7 +180,7 @@ pub const Tree = struct { | ... | @@ -180,7 +180,7 @@ pub const Tree = struct { |
| 180 | while (node.get_parent()) |*parent| { | 180 | while (node.get_parent()) |*parent| { |
| 181 | if (parent.*.is_black()) | 181 | if (parent.*.is_black()) |
| 182 | break; | 182 | break; |
| 183 | // the root is always black | 183 | // the root is always black |
| 184 | var grandpa = parent.*.get_parent() orelse unreachable; | 184 | var grandpa = parent.*.get_parent() orelse unreachable; |
| 185 | 185 | ||
| 186 | if (parent.* == grandpa.left) { | 186 | if (parent.* == grandpa.left) { |
| ... | @@ -206,7 +206,7 @@ pub const Tree = struct { | ... | @@ -206,7 +206,7 @@ pub const Tree = struct { |
| 206 | } | 206 | } |
| 207 | } else { | 207 | } else { |
| 208 | var maybe_uncle = grandpa.left; | 208 | var maybe_uncle = grandpa.left; |
| 209 | 209 | ||
| 210 | if (maybe_uncle) |uncle| { | 210 | if (maybe_uncle) |uncle| { |
| 211 | if (uncle.is_black()) | 211 | if (uncle.is_black()) |
| 212 | break; | 212 | break; |
| ... | @@ -259,7 +259,7 @@ pub const Tree = struct { | ... | @@ -259,7 +259,7 @@ pub const Tree = struct { |
| 259 | if (node.left == null) { | 259 | if (node.left == null) { |
| 260 | next = node.right.?; // Not both null as per above | 260 | next = node.right.?; // Not both null as per above |
| 261 | } else if (node.right == null) { | 261 | } else if (node.right == null) { |
| 262 | next = node.left.?; // Not both null as per above | 262 | next = node.left.?; // Not both null as per above |
| 263 | } else | 263 | } else |
| 264 | next = node.right.?.get_first(); // Just checked for null above | 264 | next = node.right.?.get_first(); // Just checked for null above |
| 265 | 265 | ||
| ... | @@ -313,7 +313,7 @@ pub const Tree = struct { | ... | @@ -313,7 +313,7 @@ pub const Tree = struct { |
| 313 | var parent = maybe_parent.?; | 313 | var parent = maybe_parent.?; |
| 314 | if (node == parent.left) { | 314 | if (node == parent.left) { |
| 315 | var sibling = parent.right.?; // Same number of black nodes. | 315 | var sibling = parent.right.?; // Same number of black nodes. |
| 316 | 316 | ||
| 317 | if (sibling.is_red()) { | 317 | if (sibling.is_red()) { |
| 318 | sibling.set_color(Black); | 318 | sibling.set_color(Black); |
| 319 | parent.set_color(Red); | 319 | parent.set_color(Red); |
| ... | @@ -321,7 +321,8 @@ pub const Tree = struct { | ... | @@ -321,7 +321,8 @@ pub const Tree = struct { |
| 321 | sibling = parent.right.?; // Just rotated | 321 | sibling = parent.right.?; // Just rotated |
| 322 | } | 322 | } |
| 323 | if ((if (sibling.left) |n| n.is_black() else true) and | 323 | if ((if (sibling.left) |n| n.is_black() else true) and |
| 324 | (if (sibling.right) |n| n.is_black() else true)) { | 324 | (if (sibling.right) |n| n.is_black() else true)) |
| 325 | { | ||
| 325 | sibling.set_color(Red); | 326 | sibling.set_color(Red); |
| 326 | node = parent; | 327 | node = parent; |
| 327 | maybe_parent = parent.get_parent(); | 328 | maybe_parent = parent.get_parent(); |
| ... | @@ -341,7 +342,7 @@ pub const Tree = struct { | ... | @@ -341,7 +342,7 @@ pub const Tree = struct { |
| 341 | break; | 342 | break; |
| 342 | } else { | 343 | } else { |
| 343 | var sibling = parent.left.?; // Same number of black nodes. | 344 | var sibling = parent.left.?; // Same number of black nodes. |
| 344 | 345 | ||
| 345 | if (sibling.is_red()) { | 346 | if (sibling.is_red()) { |
| 346 | sibling.set_color(Black); | 347 | sibling.set_color(Black); |
| 347 | parent.set_color(Red); | 348 | parent.set_color(Red); |
| ... | @@ -349,7 +350,8 @@ pub const Tree = struct { | ... | @@ -349,7 +350,8 @@ pub const Tree = struct { |
| 349 | sibling = parent.left.?; // Just rotated | 350 | sibling = parent.left.?; // Just rotated |
| 350 | } | 351 | } |
| 351 | if ((if (sibling.left) |n| n.is_black() else true) and | 352 | if ((if (sibling.left) |n| n.is_black() else true) and |
| 352 | (if (sibling.right) |n| n.is_black() else true)) { | 353 | (if (sibling.right) |n| n.is_black() else true)) |
| 354 | { | ||
| 353 | sibling.set_color(Red); | 355 | sibling.set_color(Red); |
| 354 | node = parent; | 356 | node = parent; |
| 355 | maybe_parent = parent.get_parent(); | 357 | maybe_parent = parent.get_parent(); |
| ... | @@ -397,7 +399,7 @@ pub const Tree = struct { | ... | @@ -397,7 +399,7 @@ pub const Tree = struct { |
| 397 | new.* = old.*; | 399 | new.* = old.*; |
| 398 | } | 400 | } |
| 399 | 401 | ||
| 400 | pub fn init(tree: *Tree, f: fn(*Node, *Node) mem.Compare) void { | 402 | pub fn init(tree: *Tree, f: fn (*Node, *Node) mem.Compare) void { |
| 401 | tree.root = null; | 403 | tree.root = null; |
| 402 | tree.compareFn = f; | 404 | tree.compareFn = f; |
| 403 | } | 405 | } |
std/special/build_runner.zig+2-2| ... | @@ -49,14 +49,14 @@ pub fn main() !void { | ... | @@ -49,14 +49,14 @@ pub fn main() !void { |
| 49 | 49 | ||
| 50 | var stderr_file = io.getStdErr(); | 50 | var stderr_file = io.getStdErr(); |
| 51 | var stderr_file_stream: io.FileOutStream = undefined; | 51 | var stderr_file_stream: io.FileOutStream = undefined; |
| 52 | var stderr_stream = if (stderr_file) |*f| x: { | 52 | var stderr_stream = if (stderr_file) |f| x: { |
| 53 | stderr_file_stream = io.FileOutStream.init(f); | 53 | stderr_file_stream = io.FileOutStream.init(f); |
| 54 | break :x &stderr_file_stream.stream; | 54 | break :x &stderr_file_stream.stream; |
| 55 | } else |err| err; | 55 | } else |err| err; |
| 56 | 56 | ||
| 57 | var stdout_file = io.getStdOut(); | 57 | var stdout_file = io.getStdOut(); |
| 58 | var stdout_file_stream: io.FileOutStream = undefined; | 58 | var stdout_file_stream: io.FileOutStream = undefined; |
| 59 | var stdout_stream = if (stdout_file) |*f| x: { | 59 | var stdout_stream = if (stdout_file) |f| x: { |
| 60 | stdout_file_stream = io.FileOutStream.init(f); | 60 | stdout_file_stream = io.FileOutStream.init(f); |
| 61 | break :x &stdout_file_stream.stream; | 61 | break :x &stdout_file_stream.stream; |
| 62 | } else |err| err; | 62 | } else |err| err; |
std/zig/parse.zig+56-13| ... | @@ -340,7 +340,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -340,7 +340,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 340 | const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); | 340 | const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); |
| 341 | node_ptr.* = &node.base; | 341 | node_ptr.* = &node.base; |
| 342 | 342 | ||
| 343 | stack.append(State{ .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; | 343 | try stack.append(State{ |
| 344 | .FieldListCommaOrEnd = FieldCtx{ | ||
| 345 | .doc_comments = &node.doc_comments, | ||
| 346 | .container_decl = ctx.container_decl, | ||
| 347 | }, | ||
| 348 | }); | ||
| 344 | try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.type_expr } }); | 349 | try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.type_expr } }); |
| 345 | try stack.append(State{ .ExpectToken = Token.Id.Colon }); | 350 | try stack.append(State{ .ExpectToken = Token.Id.Colon }); |
| 346 | continue; | 351 | continue; |
| ... | @@ -458,7 +463,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -458,7 +463,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 458 | const node_ptr = try container_decl.fields_and_decls.addOne(); | 463 | const node_ptr = try container_decl.fields_and_decls.addOne(); |
| 459 | node_ptr.* = &node.base; | 464 | node_ptr.* = &node.base; |
| 460 | 465 | ||
| 461 | try stack.append(State{ .FieldListCommaOrEnd = container_decl }); | 466 | try stack.append(State{ |
| 467 | .FieldListCommaOrEnd = FieldCtx{ | ||
| 468 | .doc_comments = &node.doc_comments, | ||
| 469 | .container_decl = container_decl, | ||
| 470 | }, | ||
| 471 | }); | ||
| 462 | try stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.type_expr } }); | 472 | try stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &node.type_expr } }); |
| 463 | try stack.append(State{ .ExpectToken = Token.Id.Colon }); | 473 | try stack.append(State{ .ExpectToken = Token.Id.Colon }); |
| 464 | continue; | 474 | continue; |
| ... | @@ -473,7 +483,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -473,7 +483,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 473 | }); | 483 | }); |
| 474 | try container_decl.fields_and_decls.push(&node.base); | 484 | try container_decl.fields_and_decls.push(&node.base); |
| 475 | 485 | ||
| 476 | stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable; | 486 | try stack.append(State{ |
| 487 | .FieldListCommaOrEnd = FieldCtx{ | ||
| 488 | .doc_comments = &node.doc_comments, | ||
| 489 | .container_decl = container_decl, | ||
| 490 | }, | ||
| 491 | }); | ||
| 477 | try stack.append(State{ .FieldInitValue = OptionalCtx{ .RequiredNull = &node.value_expr } }); | 492 | try stack.append(State{ .FieldInitValue = OptionalCtx{ .RequiredNull = &node.value_expr } }); |
| 478 | try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &node.type_expr } }); | 493 | try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &node.type_expr } }); |
| 479 | try stack.append(State{ .IfToken = Token.Id.Colon }); | 494 | try stack.append(State{ .IfToken = Token.Id.Colon }); |
| ... | @@ -488,7 +503,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -488,7 +503,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 488 | }); | 503 | }); |
| 489 | try container_decl.fields_and_decls.push(&node.base); | 504 | try container_decl.fields_and_decls.push(&node.base); |
| 490 | 505 | ||
| 491 | stack.append(State{ .FieldListCommaOrEnd = container_decl }) catch unreachable; | 506 | try stack.append(State{ |
| 507 | .FieldListCommaOrEnd = FieldCtx{ | ||
| 508 | .doc_comments = &node.doc_comments, | ||
| 509 | .container_decl = container_decl, | ||
| 510 | }, | ||
| 511 | }); | ||
| 492 | try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &node.value } }); | 512 | try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &node.value } }); |
| 493 | try stack.append(State{ .IfToken = Token.Id.Equal }); | 513 | try stack.append(State{ .IfToken = Token.Id.Equal }); |
| 494 | continue; | 514 | continue; |
| ... | @@ -1265,17 +1285,35 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -1265,17 +1285,35 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 1265 | }, | 1285 | }, |
| 1266 | } | 1286 | } |
| 1267 | }, | 1287 | }, |
| 1268 | State.FieldListCommaOrEnd => |container_decl| { | 1288 | State.FieldListCommaOrEnd => |field_ctx| { |
| 1269 | switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) { | 1289 | const end_token = nextToken(&tok_it, &tree); |
| 1270 | ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| { | 1290 | const end_token_index = end_token.index; |
| 1271 | container_decl.rbrace_token = end; | 1291 | const end_token_ptr = end_token.ptr; |
| 1292 | switch (end_token_ptr.id) { | ||
| 1293 | Token.Id.Comma => { | ||
| 1294 | if (eatToken(&tok_it, &tree, Token.Id.DocComment)) |doc_comment_token| { | ||
| 1295 | const loc = tree.tokenLocation(end_token_ptr.end, doc_comment_token); | ||
| 1296 | if (loc.line == 0) { | ||
| 1297 | try pushDocComment(arena, doc_comment_token, field_ctx.doc_comments); | ||
| 1298 | } else { | ||
| 1299 | prevToken(&tok_it, &tree); | ||
| 1300 | } | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | try stack.append(State{ .ContainerDecl = field_ctx.container_decl }); | ||
| 1272 | continue; | 1304 | continue; |
| 1273 | } else { | 1305 | }, |
| 1274 | try stack.append(State{ .ContainerDecl = container_decl }); | 1306 | Token.Id.RBrace => { |
| 1307 | field_ctx.container_decl.rbrace_token = end_token_index; | ||
| 1275 | continue; | 1308 | continue; |
| 1276 | }, | 1309 | }, |
| 1277 | ExpectCommaOrEndResult.parse_error => |e| { | 1310 | else => { |
| 1278 | try tree.errors.push(e); | 1311 | try tree.errors.push(Error{ |
| 1312 | .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd{ | ||
| 1313 | .token = end_token_index, | ||
| 1314 | .end_id = end_token_ptr.id, | ||
| 1315 | }, | ||
| 1316 | }); | ||
| 1279 | return tree; | 1317 | return tree; |
| 1280 | }, | 1318 | }, |
| 1281 | } | 1319 | } |
| ... | @@ -2813,6 +2851,11 @@ const ExprListCtx = struct { | ... | @@ -2813,6 +2851,11 @@ const ExprListCtx = struct { |
| 2813 | ptr: *TokenIndex, | 2851 | ptr: *TokenIndex, |
| 2814 | }; | 2852 | }; |
| 2815 | 2853 | ||
| 2854 | const FieldCtx = struct { | ||
| 2855 | container_decl: *ast.Node.ContainerDecl, | ||
| 2856 | doc_comments: *?*ast.Node.DocComment, | ||
| 2857 | }; | ||
| 2858 | |||
| 2816 | fn ListSave(comptime List: type) type { | 2859 | fn ListSave(comptime List: type) type { |
| 2817 | return struct { | 2860 | return struct { |
| 2818 | list: *List, | 2861 | list: *List, |
| ... | @@ -2950,7 +2993,7 @@ const State = union(enum) { | ... | @@ -2950,7 +2993,7 @@ const State = union(enum) { |
| 2950 | ExprListCommaOrEnd: ExprListCtx, | 2993 | ExprListCommaOrEnd: ExprListCtx, |
| 2951 | FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), | 2994 | FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), |
| 2952 | FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), | 2995 | FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList), |
| 2953 | FieldListCommaOrEnd: *ast.Node.ContainerDecl, | 2996 | FieldListCommaOrEnd: FieldCtx, |
| 2954 | FieldInitValue: OptionalCtx, | 2997 | FieldInitValue: OptionalCtx, |
| 2955 | ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), | 2998 | ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), |
| 2956 | ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), | 2999 | ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList), |
std/zig/parser_test.zig+18-1| ... | @@ -1,3 +1,20 @@ | ... | @@ -1,3 +1,20 @@ |
| 1 | test "zig fmt: correctly move doc comments on struct fields" { | ||
| 2 | try testTransform( | ||
| 3 | \\pub const section_64 = extern struct { | ||
| 4 | \\ sectname: [16]u8, /// name of this section | ||
| 5 | \\ segname: [16]u8, /// segment this section goes in | ||
| 6 | \\}; | ||
| 7 | , | ||
| 8 | \\pub const section_64 = extern struct { | ||
| 9 | \\ /// name of this section | ||
| 10 | \\ sectname: [16]u8, | ||
| 11 | \\ /// segment this section goes in | ||
| 12 | \\ segname: [16]u8, | ||
| 13 | \\}; | ||
| 14 | \\ | ||
| 15 | ); | ||
| 16 | } | ||
| 17 | |||
| 1 | test "zig fmt: preserve space between async fn definitions" { | 18 | test "zig fmt: preserve space between async fn definitions" { |
| 2 | try testCanonical( | 19 | try testCanonical( |
| 3 | \\async fn a() void {} | 20 | \\async fn a() void {} |
| ... | @@ -1848,7 +1865,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; | ... | @@ -1848,7 +1865,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 1848 | 1865 | ||
| 1849 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { | 1866 | fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *bool) ![]u8 { |
| 1850 | var stderr_file = try io.getStdErr(); | 1867 | var stderr_file = try io.getStdErr(); |
| 1851 | var stderr = &io.FileOutStream.init(&stderr_file).stream; | 1868 | var stderr = &io.FileOutStream.init(stderr_file).stream; |
| 1852 | 1869 | ||
| 1853 | var tree = try std.zig.parse(allocator, source); | 1870 | var tree = try std.zig.parse(allocator, source); |
| 1854 | defer tree.deinit(); | 1871 | defer tree.deinit(); |
test/behavior.zig+1| ... | @@ -11,6 +11,7 @@ comptime { | ... | @@ -11,6 +11,7 @@ comptime { |
| 11 | _ = @import("cases/bugs/1111.zig"); | 11 | _ = @import("cases/bugs/1111.zig"); |
| 12 | _ = @import("cases/bugs/1230.zig"); | 12 | _ = @import("cases/bugs/1230.zig"); |
| 13 | _ = @import("cases/bugs/1277.zig"); | 13 | _ = @import("cases/bugs/1277.zig"); |
| 14 | _ = @import("cases/bugs/1421.zig"); | ||
| 14 | _ = @import("cases/bugs/394.zig"); | 15 | _ = @import("cases/bugs/394.zig"); |
| 15 | _ = @import("cases/bugs/655.zig"); | 16 | _ = @import("cases/bugs/655.zig"); |
| 16 | _ = @import("cases/bugs/656.zig"); | 17 | _ = @import("cases/bugs/656.zig"); |
test/cases/bugs/1421.zig created+14| ... | @@ -0,0 +1,14 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | |||
| 5 | const S = struct { | ||
| 6 | fn method() builtin.TypeInfo { | ||
| 7 | return @typeInfo(S); | ||
| 8 | } | ||
| 9 | }; | ||
| 10 | |||
| 11 | test "functions with return type required to be comptime are generic" { | ||
| 12 | const ti = S.method(); | ||
| 13 | assert(builtin.TypeId(ti) == builtin.TypeId.Struct); | ||
| 14 | } | ||
test/cases/cast.zig+38-9| ... | @@ -487,12 +487,41 @@ fn MakeType(comptime T: type) type { | ... | @@ -487,12 +487,41 @@ fn MakeType(comptime T: type) type { |
| 487 | } | 487 | } |
| 488 | 488 | ||
| 489 | test "implicit cast from *[N]T to ?[*]T" { | 489 | test "implicit cast from *[N]T to ?[*]T" { |
| 490 | var x: ?[*]u16 = null; | ||
| 491 | var y: [4]u16 = [4]u16 {0, 1, 2, 3}; | ||
| 492 | |||
| 493 | x = &y; | ||
| 494 | assert(std.mem.eql(u16, x.?[0..4], y[0..4])); | ||
| 495 | x.?[0] = 8; | ||
| 496 | y[3] = 6; | ||
| 497 | assert(std.mem.eql(u16, x.?[0..4], y[0..4])); | ||
| 498 | } | ||
| \ No newline at end of file | |||
| 490 | var x: ?[*]u16 = null; | ||
| 491 | var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; | ||
| 492 | |||
| 493 | x = &y; | ||
| 494 | assert(std.mem.eql(u16, x.?[0..4], y[0..4])); | ||
| 495 | x.?[0] = 8; | ||
| 496 | y[3] = 6; | ||
| 497 | assert(std.mem.eql(u16, x.?[0..4], y[0..4])); | ||
| 498 | } | ||
| 499 | |||
| 500 | test "implicit cast from *T to ?*c_void" { | ||
| 501 | var a: u8 = 1; | ||
| 502 | incrementVoidPtrValue(&a); | ||
| 503 | std.debug.assert(a == 2); | ||
| 504 | } | ||
| 505 | |||
| 506 | fn incrementVoidPtrValue(value: ?*c_void) void { | ||
| 507 | @ptrCast(*u8, value.?).* += 1; | ||
| 508 | } | ||
| 509 | |||
| 510 | test "implicit cast from [*]T to ?*c_void" { | ||
| 511 | var a = []u8{ 3, 2, 1 }; | ||
| 512 | incrementVoidPtrArray(a[0..].ptr, 3); | ||
| 513 | assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); | ||
| 514 | } | ||
| 515 | |||
| 516 | fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { | ||
| 517 | var n: usize = 0; | ||
| 518 | while (n < len) : (n += 1) { | ||
| 519 | @ptrCast([*]u8, array.?)[n] += 1; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | test "*usize to *void" { | ||
| 524 | var i = usize(0); | ||
| 525 | var v = @ptrCast(*void, &i); | ||
| 526 | v.* = {}; | ||
| 527 | } | ||
test/cases/eval.zig+22| ... | @@ -652,3 +652,25 @@ fn loopNTimes(comptime n: usize) void { | ... | @@ -652,3 +652,25 @@ fn loopNTimes(comptime n: usize) void { |
| 652 | comptime var i = 0; | 652 | comptime var i = 0; |
| 653 | inline while (i < n) : (i += 1) {} | 653 | inline while (i < n) : (i += 1) {} |
| 654 | } | 654 | } |
| 655 | |||
| 656 | test "variable inside inline loop that has different types on different iterations" { | ||
| 657 | testVarInsideInlineLoop(true, u32(42)); | ||
| 658 | } | ||
| 659 | |||
| 660 | fn testVarInsideInlineLoop(args: ...) void { | ||
| 661 | comptime var i = 0; | ||
| 662 | inline while (i < args.len) : (i += 1) { | ||
| 663 | const x = args[i]; | ||
| 664 | if (i == 0) assert(x); | ||
| 665 | if (i == 1) assert(x == 42); | ||
| 666 | } | ||
| 667 | } | ||
| 668 | |||
| 669 | test "inline for with same type but different values" { | ||
| 670 | var res: usize = 0; | ||
| 671 | inline for ([]type{ [2]u8, [1]u8, [2]u8 }) |T| { | ||
| 672 | var a: T = undefined; | ||
| 673 | res += a.len; | ||
| 674 | } | ||
| 675 | assert(res == 5); | ||
| 676 | } |
test/cases/for.zig+2-4| ... | @@ -71,8 +71,7 @@ fn testBreakOuter() void { | ... | @@ -71,8 +71,7 @@ fn testBreakOuter() void { |
| 71 | var array = "aoeu"; | 71 | var array = "aoeu"; |
| 72 | var count: usize = 0; | 72 | var count: usize = 0; |
| 73 | outer: for (array) |_| { | 73 | outer: for (array) |_| { |
| 74 | // TODO shouldn't get error for redeclaring "_" | 74 | for (array) |_| { |
| 75 | for (array) |_2| { | ||
| 76 | count += 1; | 75 | count += 1; |
| 77 | break :outer; | 76 | break :outer; |
| 78 | } | 77 | } |
| ... | @@ -89,8 +88,7 @@ fn testContinueOuter() void { | ... | @@ -89,8 +88,7 @@ fn testContinueOuter() void { |
| 89 | var array = "aoeu"; | 88 | var array = "aoeu"; |
| 90 | var counter: usize = 0; | 89 | var counter: usize = 0; |
| 91 | outer: for (array) |_| { | 90 | outer: for (array) |_| { |
| 92 | // TODO shouldn't get error for redeclaring "_" | 91 | for (array) |_| { |
| 93 | for (array) |_2| { | ||
| 94 | counter += 1; | 92 | counter += 1; |
| 95 | continue :outer; | 93 | continue :outer; |
| 96 | } | 94 | } |
test/compare_output.zig+15-15| ... | @@ -19,7 +19,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -19,7 +19,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 19 | \\ | 19 | \\ |
| 20 | \\pub fn main() void { | 20 | \\pub fn main() void { |
| 21 | \\ privateFunction(); | 21 | \\ privateFunction(); |
| 22 | \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream); | 22 | \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream; |
| 23 | \\ stdout.print("OK 2\n") catch unreachable; | 23 | \\ stdout.print("OK 2\n") catch unreachable; |
| 24 | \\} | 24 | \\} |
| 25 | \\ | 25 | \\ |
| ... | @@ -34,7 +34,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -34,7 +34,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 34 | \\// purposefully conflicting function with main.zig | 34 | \\// purposefully conflicting function with main.zig |
| 35 | \\// but it's private so it should be OK | 35 | \\// but it's private so it should be OK |
| 36 | \\fn privateFunction() void { | 36 | \\fn privateFunction() void { |
| 37 | \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream); | 37 | \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream; |
| 38 | \\ stdout.print("OK 1\n") catch unreachable; | 38 | \\ stdout.print("OK 1\n") catch unreachable; |
| 39 | \\} | 39 | \\} |
| 40 | \\ | 40 | \\ |
| ... | @@ -60,7 +60,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -60,7 +60,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 60 | tc.addSourceFile("foo.zig", | 60 | tc.addSourceFile("foo.zig", |
| 61 | \\use @import("std").io; | 61 | \\use @import("std").io; |
| 62 | \\pub fn foo_function() void { | 62 | \\pub fn foo_function() void { |
| 63 | \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream); | 63 | \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream; |
| 64 | \\ stdout.print("OK\n") catch unreachable; | 64 | \\ stdout.print("OK\n") catch unreachable; |
| 65 | \\} | 65 | \\} |
| 66 | ); | 66 | ); |
| ... | @@ -71,7 +71,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -71,7 +71,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 71 | \\ | 71 | \\ |
| 72 | \\pub fn bar_function() void { | 72 | \\pub fn bar_function() void { |
| 73 | \\ if (foo_function()) { | 73 | \\ if (foo_function()) { |
| 74 | \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream); | 74 | \\ const stdout = &FileOutStream.init(getStdOut() catch unreachable).stream; |
| 75 | \\ stdout.print("OK\n") catch unreachable; | 75 | \\ stdout.print("OK\n") catch unreachable; |
| 76 | \\ } | 76 | \\ } |
| 77 | \\} | 77 | \\} |
| ... | @@ -103,7 +103,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -103,7 +103,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 103 | \\pub const a_text = "OK\n"; | 103 | \\pub const a_text = "OK\n"; |
| 104 | \\ | 104 | \\ |
| 105 | \\pub fn ok() void { | 105 | \\pub fn ok() void { |
| 106 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 106 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 107 | \\ stdout.print(b_text) catch unreachable; | 107 | \\ stdout.print(b_text) catch unreachable; |
| 108 | \\} | 108 | \\} |
| 109 | ); | 109 | ); |
| ... | @@ -121,7 +121,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -121,7 +121,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 121 | \\const io = @import("std").io; | 121 | \\const io = @import("std").io; |
| 122 | \\ | 122 | \\ |
| 123 | \\pub fn main() void { | 123 | \\pub fn main() void { |
| 124 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 124 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 125 | \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable; | 125 | \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable; |
| 126 | \\} | 126 | \\} |
| 127 | , "Hello, world!\n0012 012 a\n"); | 127 | , "Hello, world!\n0012 012 a\n"); |
| ... | @@ -274,7 +274,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -274,7 +274,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 274 | \\ var x_local : i32 = print_ok(x); | 274 | \\ var x_local : i32 = print_ok(x); |
| 275 | \\} | 275 | \\} |
| 276 | \\fn print_ok(val: @typeOf(x)) @typeOf(foo) { | 276 | \\fn print_ok(val: @typeOf(x)) @typeOf(foo) { |
| 277 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 277 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 278 | \\ stdout.print("OK\n") catch unreachable; | 278 | \\ stdout.print("OK\n") catch unreachable; |
| 279 | \\ return 0; | 279 | \\ return 0; |
| 280 | \\} | 280 | \\} |
| ... | @@ -356,7 +356,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -356,7 +356,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 356 | \\pub fn main() void { | 356 | \\pub fn main() void { |
| 357 | \\ const bar = Bar {.field2 = 13,}; | 357 | \\ const bar = Bar {.field2 = 13,}; |
| 358 | \\ const foo = Foo {.field1 = bar,}; | 358 | \\ const foo = Foo {.field1 = bar,}; |
| 359 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 359 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 360 | \\ if (!foo.method()) { | 360 | \\ if (!foo.method()) { |
| 361 | \\ stdout.print("BAD\n") catch unreachable; | 361 | \\ stdout.print("BAD\n") catch unreachable; |
| 362 | \\ } | 362 | \\ } |
| ... | @@ -370,7 +370,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -370,7 +370,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 370 | cases.add("defer with only fallthrough", | 370 | cases.add("defer with only fallthrough", |
| 371 | \\const io = @import("std").io; | 371 | \\const io = @import("std").io; |
| 372 | \\pub fn main() void { | 372 | \\pub fn main() void { |
| 373 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 373 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 374 | \\ stdout.print("before\n") catch unreachable; | 374 | \\ stdout.print("before\n") catch unreachable; |
| 375 | \\ defer stdout.print("defer1\n") catch unreachable; | 375 | \\ defer stdout.print("defer1\n") catch unreachable; |
| 376 | \\ defer stdout.print("defer2\n") catch unreachable; | 376 | \\ defer stdout.print("defer2\n") catch unreachable; |
| ... | @@ -383,7 +383,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -383,7 +383,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 383 | \\const io = @import("std").io; | 383 | \\const io = @import("std").io; |
| 384 | \\const os = @import("std").os; | 384 | \\const os = @import("std").os; |
| 385 | \\pub fn main() void { | 385 | \\pub fn main() void { |
| 386 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 386 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 387 | \\ stdout.print("before\n") catch unreachable; | 387 | \\ stdout.print("before\n") catch unreachable; |
| 388 | \\ defer stdout.print("defer1\n") catch unreachable; | 388 | \\ defer stdout.print("defer1\n") catch unreachable; |
| 389 | \\ defer stdout.print("defer2\n") catch unreachable; | 389 | \\ defer stdout.print("defer2\n") catch unreachable; |
| ... | @@ -400,7 +400,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -400,7 +400,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 400 | \\ do_test() catch return; | 400 | \\ do_test() catch return; |
| 401 | \\} | 401 | \\} |
| 402 | \\fn do_test() !void { | 402 | \\fn do_test() !void { |
| 403 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 403 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 404 | \\ stdout.print("before\n") catch unreachable; | 404 | \\ stdout.print("before\n") catch unreachable; |
| 405 | \\ defer stdout.print("defer1\n") catch unreachable; | 405 | \\ defer stdout.print("defer1\n") catch unreachable; |
| 406 | \\ errdefer stdout.print("deferErr\n") catch unreachable; | 406 | \\ errdefer stdout.print("deferErr\n") catch unreachable; |
| ... | @@ -419,7 +419,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -419,7 +419,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 419 | \\ do_test() catch return; | 419 | \\ do_test() catch return; |
| 420 | \\} | 420 | \\} |
| 421 | \\fn do_test() !void { | 421 | \\fn do_test() !void { |
| 422 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 422 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 423 | \\ stdout.print("before\n") catch unreachable; | 423 | \\ stdout.print("before\n") catch unreachable; |
| 424 | \\ defer stdout.print("defer1\n") catch unreachable; | 424 | \\ defer stdout.print("defer1\n") catch unreachable; |
| 425 | \\ errdefer stdout.print("deferErr\n") catch unreachable; | 425 | \\ errdefer stdout.print("deferErr\n") catch unreachable; |
| ... | @@ -436,7 +436,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -436,7 +436,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 436 | \\const io = @import("std").io; | 436 | \\const io = @import("std").io; |
| 437 | \\ | 437 | \\ |
| 438 | \\pub fn main() void { | 438 | \\pub fn main() void { |
| 439 | \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); | 439 | \\ const stdout = &io.FileOutStream.init(io.getStdOut() catch unreachable).stream; |
| 440 | \\ stdout.print(foo_txt) catch unreachable; | 440 | \\ stdout.print(foo_txt) catch unreachable; |
| 441 | \\} | 441 | \\} |
| 442 | , "1234\nabcd\n"); | 442 | , "1234\nabcd\n"); |
| ... | @@ -456,7 +456,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -456,7 +456,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 456 | \\pub fn main() !void { | 456 | \\pub fn main() !void { |
| 457 | \\ var args_it = os.args(); | 457 | \\ var args_it = os.args(); |
| 458 | \\ var stdout_file = try io.getStdOut(); | 458 | \\ var stdout_file = try io.getStdOut(); |
| 459 | \\ var stdout_adapter = io.FileOutStream.init(&stdout_file); | 459 | \\ var stdout_adapter = io.FileOutStream.init(stdout_file); |
| 460 | \\ const stdout = &stdout_adapter.stream; | 460 | \\ const stdout = &stdout_adapter.stream; |
| 461 | \\ var index: usize = 0; | 461 | \\ var index: usize = 0; |
| 462 | \\ _ = args_it.skip(); | 462 | \\ _ = args_it.skip(); |
| ... | @@ -497,7 +497,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -497,7 +497,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 497 | \\pub fn main() !void { | 497 | \\pub fn main() !void { |
| 498 | \\ var args_it = os.args(); | 498 | \\ var args_it = os.args(); |
| 499 | \\ var stdout_file = try io.getStdOut(); | 499 | \\ var stdout_file = try io.getStdOut(); |
| 500 | \\ var stdout_adapter = io.FileOutStream.init(&stdout_file); | 500 | \\ var stdout_adapter = io.FileOutStream.init(stdout_file); |
| 501 | \\ const stdout = &stdout_adapter.stream; | 501 | \\ const stdout = &stdout_adapter.stream; |
| 502 | \\ var index: usize = 0; | 502 | \\ var index: usize = 0; |
| 503 | \\ _ = args_it.skip(); | 503 | \\ _ = args_it.skip(); |
test/compile_errors.zig+43| ... | @@ -1,6 +1,49 @@ | ... | @@ -1,6 +1,49 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | ||
| 5 | "switch with invalid expression parameter", | ||
| 6 | \\export fn entry() void { | ||
| 7 | \\ Test(i32); | ||
| 8 | \\} | ||
| 9 | \\fn Test(comptime T: type) void { | ||
| 10 | \\ const x = switch (T) { | ||
| 11 | \\ []u8 => |x| 123, | ||
| 12 | \\ i32 => |x| 456, | ||
| 13 | \\ else => unreachable, | ||
| 14 | \\ }; | ||
| 15 | \\} | ||
| 16 | , | ||
| 17 | ".tmp_source.zig:7:17: error: switch on type 'type' provides no expression parameter", | ||
| 18 | ); | ||
| 19 | |||
| 20 | cases.add( | ||
| 21 | "function protoype with no body", | ||
| 22 | \\fn foo() void; | ||
| 23 | \\export fn entry() void { | ||
| 24 | \\ foo(); | ||
| 25 | \\} | ||
| 26 | , | ||
| 27 | ".tmp_source.zig:1:1: error: non-extern function has no body", | ||
| 28 | ); | ||
| 29 | |||
| 30 | cases.add( | ||
| 31 | "@typeInfo causing depend on itself compile error", | ||
| 32 | \\const start = struct { | ||
| 33 | \\ fn crash() bug() { | ||
| 34 | \\ return bug; | ||
| 35 | \\ } | ||
| 36 | \\}; | ||
| 37 | \\fn bug() void { | ||
| 38 | \\ _ = @typeInfo(start).Struct; | ||
| 39 | \\} | ||
| 40 | \\export fn entry() void { | ||
| 41 | \\ var boom = start.crash(); | ||
| 42 | \\} | ||
| 43 | , | ||
| 44 | ".tmp_source.zig:2:5: error: 'crash' depends on itself", | ||
| 45 | ); | ||
| 46 | |||
| 4 | cases.add( | 47 | cases.add( |
| 5 | "@handle() called outside of function definition", | 48 | "@handle() called outside of function definition", |
| 6 | \\var handle_undef: promise = undefined; | 49 | \\var handle_undef: promise = undefined; |
test/tests.zig+6-6| ... | @@ -263,8 +263,8 @@ pub const CompareOutputContext = struct { | ... | @@ -263,8 +263,8 @@ pub const CompareOutputContext = struct { |
| 263 | var stdout = Buffer.initNull(b.allocator); | 263 | var stdout = Buffer.initNull(b.allocator); |
| 264 | var stderr = Buffer.initNull(b.allocator); | 264 | var stderr = Buffer.initNull(b.allocator); |
| 265 | 265 | ||
| 266 | var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); | 266 | var stdout_file_in_stream = io.FileInStream.init(child.stdout.?); |
| 267 | var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); | 267 | var stderr_file_in_stream = io.FileInStream.init(child.stderr.?); |
| 268 | 268 | ||
| 269 | stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; | 269 | stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; |
| 270 | stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; | 270 | stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; |
| ... | @@ -578,8 +578,8 @@ pub const CompileErrorContext = struct { | ... | @@ -578,8 +578,8 @@ pub const CompileErrorContext = struct { |
| 578 | var stdout_buf = Buffer.initNull(b.allocator); | 578 | var stdout_buf = Buffer.initNull(b.allocator); |
| 579 | var stderr_buf = Buffer.initNull(b.allocator); | 579 | var stderr_buf = Buffer.initNull(b.allocator); |
| 580 | 580 | ||
| 581 | var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); | 581 | var stdout_file_in_stream = io.FileInStream.init(child.stdout.?); |
| 582 | var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); | 582 | var stderr_file_in_stream = io.FileInStream.init(child.stderr.?); |
| 583 | 583 | ||
| 584 | stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; | 584 | stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; |
| 585 | stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; | 585 | stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; |
| ... | @@ -842,8 +842,8 @@ pub const TranslateCContext = struct { | ... | @@ -842,8 +842,8 @@ pub const TranslateCContext = struct { |
| 842 | var stdout_buf = Buffer.initNull(b.allocator); | 842 | var stdout_buf = Buffer.initNull(b.allocator); |
| 843 | var stderr_buf = Buffer.initNull(b.allocator); | 843 | var stderr_buf = Buffer.initNull(b.allocator); |
| 844 | 844 | ||
| 845 | var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?); | 845 | var stdout_file_in_stream = io.FileInStream.init(child.stdout.?); |
| 846 | var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?); | 846 | var stderr_file_in_stream = io.FileInStream.init(child.stderr.?); |
| 847 | 847 | ||
| 848 | stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; | 848 | stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; |
| 849 | stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; | 849 | stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; |