| author | |
| committer | |
| log | b9e1fef5628f01892cf09ade4425625a2cf34911 |
| tree | 53c98c274d22bcd956ff5affa983ee80c6be9fb7 |
| parent | 5667435bc4eacb36b4c33470784f1662637341b4 |
| parent | aac26f3b31ddb43e863ac7186fd19a7e251c1b8a |
87 files changed, 2544 insertions(+), 2345 deletions(-)
.github/workflows/ci.yaml+18| ... | ... | @@ -50,6 +50,24 @@ jobs: |
| 50 | 50 | uses: actions/checkout@v4 |
| 51 | 51 | - name: Build and Test |
| 52 | 52 | run: sh ci/aarch64-linux-release.sh |
| 53 | riscv64-linux-debug: | |
| 54 | if: github.event_name == 'push' | |
| 55 | timeout-minutes: 420 | |
| 56 | runs-on: [self-hosted, Linux, riscv64] | |
| 57 | steps: | |
| 58 | - name: Checkout | |
| 59 | uses: actions/checkout@v4 | |
| 60 | - name: Build and Test | |
| 61 | run: sh ci/riscv64-linux-debug.sh | |
| 62 | riscv64-linux-release: | |
| 63 | if: github.event_name == 'push' | |
| 64 | timeout-minutes: 420 | |
| 65 | runs-on: [self-hosted, Linux, riscv64] | |
| 66 | steps: | |
| 67 | - name: Checkout | |
| 68 | uses: actions/checkout@v4 | |
| 69 | - name: Build and Test | |
| 70 | run: sh ci/riscv64-linux-release.sh | |
| 53 | 71 | x86_64-macos-release: |
| 54 | 72 | runs-on: "macos-13" |
| 55 | 73 | env: |
.github/workflows/riscv.yaml deleted-22| ... | ... | @@ -1,22 +0,0 @@ |
| 1 | name: riscv | |
| 2 | on: | |
| 3 | workflow_dispatch: | |
| 4 | permissions: | |
| 5 | contents: read | |
| 6 | jobs: | |
| 7 | riscv64-linux-debug: | |
| 8 | timeout-minutes: 1020 | |
| 9 | runs-on: [self-hosted, Linux, riscv64] | |
| 10 | steps: | |
| 11 | - name: Checkout | |
| 12 | uses: actions/checkout@v4 | |
| 13 | - name: Build and Test | |
| 14 | run: sh ci/riscv64-linux-debug.sh | |
| 15 | riscv64-linux-release: | |
| 16 | timeout-minutes: 900 | |
| 17 | runs-on: [self-hosted, Linux, riscv64] | |
| 18 | steps: | |
| 19 | - name: Checkout | |
| 20 | uses: actions/checkout@v4 | |
| 21 | - name: Build and Test | |
| 22 | run: sh ci/riscv64-linux-release.sh |
build.zig+2-20| ... | ... | @@ -90,6 +90,7 @@ pub fn build(b: *std.Build) !void { |
| 90 | 90 | const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false; |
| 91 | 91 | const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false; |
| 92 | 92 | const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false; |
| 93 | const skip_compile_errors = b.option(bool, "skip-compile-errors", "Main test suite skips compile error tests") orelse false; | |
| 93 | 94 | const skip_translate_c = b.option(bool, "skip-translate-c", "Main test suite skips translate-c tests") orelse false; |
| 94 | 95 | const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false; |
| 95 | 96 | const skip_freebsd = b.option(bool, "skip-freebsd", "Main test suite skips targets with freebsd OS") orelse false; |
| ... | ... | @@ -418,6 +419,7 @@ pub fn build(b: *std.Build) !void { |
| 418 | 419 | try tests.addCases(b, test_cases_step, target, .{ |
| 419 | 420 | .test_filters = test_filters, |
| 420 | 421 | .test_target_filters = test_target_filters, |
| 422 | .skip_compile_errors = skip_compile_errors, | |
| 421 | 423 | .skip_non_native = skip_non_native, |
| 422 | 424 | .skip_freebsd = skip_freebsd, |
| 423 | 425 | .skip_netbsd = skip_netbsd, |
| ... | ... | @@ -450,7 +452,6 @@ pub fn build(b: *std.Build) !void { |
| 450 | 452 | .desc = "Run the behavior tests", |
| 451 | 453 | .optimize_modes = optimization_modes, |
| 452 | 454 | .include_paths = &.{}, |
| 453 | .windows_libs = &.{}, | |
| 454 | 455 | .skip_single_threaded = skip_single_threaded, |
| 455 | 456 | .skip_non_native = skip_non_native, |
| 456 | 457 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -473,7 +474,6 @@ pub fn build(b: *std.Build) !void { |
| 473 | 474 | .desc = "Run the @cImport tests", |
| 474 | 475 | .optimize_modes = optimization_modes, |
| 475 | 476 | .include_paths = &.{"test/c_import"}, |
| 476 | .windows_libs = &.{}, | |
| 477 | 477 | .skip_single_threaded = true, |
| 478 | 478 | .skip_non_native = skip_non_native, |
| 479 | 479 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -494,7 +494,6 @@ pub fn build(b: *std.Build) !void { |
| 494 | 494 | .desc = "Run the compiler_rt tests", |
| 495 | 495 | .optimize_modes = optimization_modes, |
| 496 | 496 | .include_paths = &.{}, |
| 497 | .windows_libs = &.{}, | |
| 498 | 497 | .skip_single_threaded = true, |
| 499 | 498 | .skip_non_native = skip_non_native, |
| 500 | 499 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -516,7 +515,6 @@ pub fn build(b: *std.Build) !void { |
| 516 | 515 | .desc = "Run the zigc tests", |
| 517 | 516 | .optimize_modes = optimization_modes, |
| 518 | 517 | .include_paths = &.{}, |
| 519 | .windows_libs = &.{}, | |
| 520 | 518 | .skip_single_threaded = true, |
| 521 | 519 | .skip_non_native = skip_non_native, |
| 522 | 520 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -538,12 +536,6 @@ pub fn build(b: *std.Build) !void { |
| 538 | 536 | .desc = "Run the standard library tests", |
| 539 | 537 | .optimize_modes = optimization_modes, |
| 540 | 538 | .include_paths = &.{}, |
| 541 | .windows_libs = &.{ | |
| 542 | "advapi32", | |
| 543 | "crypt32", | |
| 544 | "iphlpapi", | |
| 545 | "ws2_32", | |
| 546 | }, | |
| 547 | 539 | .skip_single_threaded = skip_single_threaded, |
| 548 | 540 | .skip_non_native = skip_non_native, |
| 549 | 541 | .skip_freebsd = skip_freebsd, |
| ... | ... | @@ -741,12 +733,6 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu |
| 741 | 733 | compiler_mod.addImport("aro", aro_mod); |
| 742 | 734 | compiler_mod.addImport("aro_translate_c", aro_translate_c_mod); |
| 743 | 735 | |
| 744 | if (options.target.result.os.tag == .windows) { | |
| 745 | compiler_mod.linkSystemLibrary("advapi32", .{}); | |
| 746 | compiler_mod.linkSystemLibrary("crypt32", .{}); | |
| 747 | compiler_mod.linkSystemLibrary("ws2_32", .{}); | |
| 748 | } | |
| 749 | ||
| 750 | 736 | return compiler_mod; |
| 751 | 737 | } |
| 752 | 738 | |
| ... | ... | @@ -1444,10 +1430,6 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { |
| 1444 | 1430 | }), |
| 1445 | 1431 | }); |
| 1446 | 1432 | |
| 1447 | if (b.graph.host.result.os.tag == .windows) { | |
| 1448 | doctest_exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 1449 | } | |
| 1450 | ||
| 1451 | 1433 | var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| { |
| 1452 | 1434 | std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{ |
| 1453 | 1435 | b.build_root, @errorName(err), |
ci/riscv64-linux-debug.sh+2-1| ... | ... | @@ -49,11 +49,12 @@ unset CXX |
| 49 | 49 | ninja install |
| 50 | 50 | |
| 51 | 51 | # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. |
| 52 | stage3-debug/bin/zig build test-cases test-modules test-unit test-standalone test-c-abi test-link test-stack-traces test-asm-link test-llvm-ir \ | |
| 52 | stage3-debug/bin/zig build test-cases test-modules test-unit test-c-abi test-stack-traces test-asm-link test-llvm-ir \ | |
| 53 | 53 | --maxrss 68719476736 \ |
| 54 | 54 | -Dstatic-llvm \ |
| 55 | 55 | -Dskip-non-native \ |
| 56 | 56 | -Dskip-single-threaded \ |
| 57 | -Dskip-compile-errors \ | |
| 57 | 58 | -Dskip-translate-c \ |
| 58 | 59 | -Dskip-run-translated-c \ |
| 59 | 60 | -Dtarget=native-native-musl \ |
ci/riscv64-linux-release.sh+2-1| ... | ... | @@ -49,11 +49,12 @@ unset CXX |
| 49 | 49 | ninja install |
| 50 | 50 | |
| 51 | 51 | # No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts. |
| 52 | stage3-release/bin/zig build test-cases test-modules test-unit test-standalone test-c-abi test-link test-stack-traces test-asm-link test-llvm-ir \ | |
| 52 | stage3-release/bin/zig build test-cases test-modules test-unit test-c-abi test-stack-traces test-asm-link test-llvm-ir \ | |
| 53 | 53 | --maxrss 68719476736 \ |
| 54 | 54 | -Dstatic-llvm \ |
| 55 | 55 | -Dskip-non-native \ |
| 56 | 56 | -Dskip-single-threaded \ |
| 57 | -Dskip-compile-errors \ | |
| 57 | 58 | -Dskip-translate-c \ |
| 58 | 59 | -Dskip-run-translated-c \ |
| 59 | 60 | -Dtarget=native-native-musl \ |
doc/langref/test_switch_dispatch_loop.zig+5-3| ... | ... | @@ -8,20 +8,22 @@ const Instruction = enum { |
| 8 | 8 | }; |
| 9 | 9 | |
| 10 | 10 | fn evaluate(initial_stack: []const i32, code: []const Instruction) !i32 { |
| 11 | var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack); | |
| 11 | var buffer: [8]i32 = undefined; | |
| 12 | var stack = std.ArrayListUnmanaged(i32).initBuffer(&buffer); | |
| 13 | try stack.appendSliceBounded(initial_stack); | |
| 12 | 14 | var ip: usize = 0; |
| 13 | 15 | |
| 14 | 16 | return vm: switch (code[ip]) { |
| 15 | 17 | // Because all code after `continue` is unreachable, this branch does |
| 16 | 18 | // not provide a result. |
| 17 | 19 | .add => { |
| 18 | try stack.append(stack.pop().? + stack.pop().?); | |
| 20 | try stack.appendBounded(stack.pop().? + stack.pop().?); | |
| 19 | 21 | |
| 20 | 22 | ip += 1; |
| 21 | 23 | continue :vm code[ip]; |
| 22 | 24 | }, |
| 23 | 25 | .mul => { |
| 24 | try stack.append(stack.pop().? * stack.pop().?); | |
| 26 | try stack.appendBounded(stack.pop().? * stack.pop().?); | |
| 25 | 27 | |
| 26 | 28 | ip += 1; |
| 27 | 29 | continue :vm code[ip]; |
lib/compiler/aro/aro/Attribute.zig+1-1| ... | ... | @@ -708,7 +708,7 @@ pub const Arguments = blk: { |
| 708 | 708 | field.* = .{ |
| 709 | 709 | .name = decl.name, |
| 710 | 710 | .type = @field(attributes, decl.name), |
| 711 | .alignment = 0, | |
| 711 | .alignment = @alignOf(@field(attributes, decl.name)), | |
| 712 | 712 | }; |
| 713 | 713 | } |
| 714 | 714 |
lib/compiler/build_runner.zig+4-1| ... | ... | @@ -502,6 +502,9 @@ pub fn main() !void { |
| 502 | 502 | }; |
| 503 | 503 | } |
| 504 | 504 | |
| 505 | // Comptime-known guard to prevent including the logic below when `!Watch.have_impl`. | |
| 506 | if (!Watch.have_impl) unreachable; | |
| 507 | ||
| 505 | 508 | try w.update(gpa, run.step_stack.keys()); |
| 506 | 509 | |
| 507 | 510 | // Wait until a file system notification arrives. Read all such events |
| ... | ... | @@ -511,7 +514,7 @@ pub fn main() !void { |
| 511 | 514 | // recursive dependants. |
| 512 | 515 | var caption_buf: [std.Progress.Node.max_name_len]u8 = undefined; |
| 513 | 516 | const caption = std.fmt.bufPrint(&caption_buf, "watching {d} directories, {d} processes", .{ |
| 514 | w.dir_table.entries.len, countSubProcesses(run.step_stack.keys()), | |
| 517 | w.dir_count, countSubProcesses(run.step_stack.keys()), | |
| 515 | 518 | }) catch &caption_buf; |
| 516 | 519 | var debouncing_node = main_progress_node.start(caption, 0); |
| 517 | 520 | var in_debounce = false; |
lib/compiler/resinator/cli.zig+7-5| ... | ... | @@ -1141,6 +1141,8 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn |
| 1141 | 1141 | } |
| 1142 | 1142 | output_format = .res; |
| 1143 | 1143 | } |
| 1144 | } else { | |
| 1145 | output_format_source = .output_format_arg; | |
| 1144 | 1146 | } |
| 1145 | 1147 | options.output_source = .{ .filename = try filepathWithExtension(allocator, options.input_source.filename, output_format.?.extension()) }; |
| 1146 | 1148 | } else { |
| ... | ... | @@ -1529,21 +1531,21 @@ fn testParseOutput(args: []const []const u8, expected_output: []const u8) !?Opti |
| 1529 | 1531 | var diagnostics = Diagnostics.init(std.testing.allocator); |
| 1530 | 1532 | defer diagnostics.deinit(); |
| 1531 | 1533 | |
| 1532 | var output = std.ArrayList(u8).init(std.testing.allocator); | |
| 1534 | var output: std.io.Writer.Allocating = .init(std.testing.allocator); | |
| 1533 | 1535 | defer output.deinit(); |
| 1534 | 1536 | |
| 1535 | 1537 | var options = parse(std.testing.allocator, args, &diagnostics) catch |err| switch (err) { |
| 1536 | 1538 | error.ParseError => { |
| 1537 | try diagnostics.renderToWriter(args, output.writer(), .no_color); | |
| 1538 | try std.testing.expectEqualStrings(expected_output, output.items); | |
| 1539 | try diagnostics.renderToWriter(args, &output.writer, .no_color); | |
| 1540 | try std.testing.expectEqualStrings(expected_output, output.getWritten()); | |
| 1539 | 1541 | return null; |
| 1540 | 1542 | }, |
| 1541 | 1543 | else => |e| return e, |
| 1542 | 1544 | }; |
| 1543 | 1545 | errdefer options.deinit(); |
| 1544 | 1546 | |
| 1545 | try diagnostics.renderToWriter(args, output.writer(), .no_color); | |
| 1546 | try std.testing.expectEqualStrings(expected_output, output.items); | |
| 1547 | try diagnostics.renderToWriter(args, &output.writer, .no_color); | |
| 1548 | try std.testing.expectEqualStrings(expected_output, output.getWritten()); | |
| 1547 | 1549 | return options; |
| 1548 | 1550 | } |
| 1549 | 1551 |
lib/compiler/resinator/compile.zig+50-46| ... | ... | @@ -550,7 +550,7 @@ pub const Compiler = struct { |
| 550 | 550 | // so get it here to simplify future usage. |
| 551 | 551 | const filename_token = node.filename.getFirstToken(); |
| 552 | 552 | |
| 553 | const file = self.searchForFile(filename_utf8) catch |err| switch (err) { | |
| 553 | const file_handle = self.searchForFile(filename_utf8) catch |err| switch (err) { | |
| 554 | 554 | error.OutOfMemory => |e| return e, |
| 555 | 555 | else => |e| { |
| 556 | 556 | const filename_string_index = try self.diagnostics.putString(filename_utf8); |
| ... | ... | @@ -564,13 +564,15 @@ pub const Compiler = struct { |
| 564 | 564 | }); |
| 565 | 565 | }, |
| 566 | 566 | }; |
| 567 | defer file.close(); | |
| 567 | defer file_handle.close(); | |
| 568 | var file_buffer: [2048]u8 = undefined; | |
| 569 | var file_reader = file_handle.reader(&file_buffer); | |
| 568 | 570 | |
| 569 | 571 | if (maybe_predefined_type) |predefined_type| { |
| 570 | 572 | switch (predefined_type) { |
| 571 | 573 | .GROUP_ICON, .GROUP_CURSOR => { |
| 572 | 574 | // Check for animated icon first |
| 573 | if (ani.isAnimatedIcon(file.deprecatedReader())) { | |
| 575 | if (ani.isAnimatedIcon(file_reader.interface.adaptToOldInterface())) { | |
| 574 | 576 | // Animated icons are just put into the resource unmodified, |
| 575 | 577 | // and the resource type changes to ANIICON/ANICURSOR |
| 576 | 578 | |
| ... | ... | @@ -582,18 +584,18 @@ pub const Compiler = struct { |
| 582 | 584 | header.type_value.ordinal = @intFromEnum(new_predefined_type); |
| 583 | 585 | header.memory_flags = MemoryFlags.defaults(new_predefined_type); |
| 584 | 586 | header.applyMemoryFlags(node.common_resource_attributes, self.source); |
| 585 | header.data_size = @intCast(try file.getEndPos()); | |
| 587 | header.data_size = @intCast(try file_reader.getSize()); | |
| 586 | 588 | |
| 587 | 589 | try header.write(writer, self.errContext(node.id)); |
| 588 | try file.seekTo(0); | |
| 589 | try writeResourceData(writer, file.deprecatedReader(), header.data_size); | |
| 590 | try file_reader.seekTo(0); | |
| 591 | try writeResourceData(writer, &file_reader.interface, header.data_size); | |
| 590 | 592 | return; |
| 591 | 593 | } |
| 592 | 594 | |
| 593 | 595 | // isAnimatedIcon moved the file cursor so reset to the start |
| 594 | try file.seekTo(0); | |
| 596 | try file_reader.seekTo(0); | |
| 595 | 597 | |
| 596 | const icon_dir = ico.read(self.allocator, file.deprecatedReader(), try file.getEndPos()) catch |err| switch (err) { | |
| 598 | const icon_dir = ico.read(self.allocator, file_reader.interface.adaptToOldInterface(), try file_reader.getSize()) catch |err| switch (err) { | |
| 597 | 599 | error.OutOfMemory => |e| return e, |
| 598 | 600 | else => |e| { |
| 599 | 601 | return self.iconReadError( |
| ... | ... | @@ -671,15 +673,15 @@ pub const Compiler = struct { |
| 671 | 673 | try writer.writeInt(u16, entry.type_specific_data.cursor.hotspot_y, .little); |
| 672 | 674 | } |
| 673 | 675 | |
| 674 | try file.seekTo(entry.data_offset_from_start_of_file); | |
| 675 | var header_bytes = file.deprecatedReader().readBytesNoEof(16) catch { | |
| 676 | try file_reader.seekTo(entry.data_offset_from_start_of_file); | |
| 677 | var header_bytes = (file_reader.interface.takeArray(16) catch { | |
| 676 | 678 | return self.iconReadError( |
| 677 | 679 | error.UnexpectedEOF, |
| 678 | 680 | filename_utf8, |
| 679 | 681 | filename_token, |
| 680 | 682 | predefined_type, |
| 681 | 683 | ); |
| 682 | }; | |
| 684 | }).*; | |
| 683 | 685 | |
| 684 | 686 | const image_format = ico.ImageFormat.detect(&header_bytes); |
| 685 | 687 | if (!image_format.validate(&header_bytes)) { |
| ... | ... | @@ -802,8 +804,8 @@ pub const Compiler = struct { |
| 802 | 804 | }, |
| 803 | 805 | } |
| 804 | 806 | |
| 805 | try file.seekTo(entry.data_offset_from_start_of_file); | |
| 806 | try writeResourceDataNoPadding(writer, file.deprecatedReader(), entry.data_size_in_bytes); | |
| 807 | try file_reader.seekTo(entry.data_offset_from_start_of_file); | |
| 808 | try writeResourceDataNoPadding(writer, &file_reader.interface, entry.data_size_in_bytes); | |
| 807 | 809 | try writeDataPadding(writer, full_data_size); |
| 808 | 810 | |
| 809 | 811 | if (self.state.icon_id == std.math.maxInt(u16)) { |
| ... | ... | @@ -857,9 +859,9 @@ pub const Compiler = struct { |
| 857 | 859 | }, |
| 858 | 860 | .BITMAP => { |
| 859 | 861 | header.applyMemoryFlags(node.common_resource_attributes, self.source); |
| 860 | const file_size = try file.getEndPos(); | |
| 862 | const file_size = try file_reader.getSize(); | |
| 861 | 863 | |
| 862 | const bitmap_info = bmp.read(file.deprecatedReader(), file_size) catch |err| { | |
| 864 | const bitmap_info = bmp.read(file_reader.interface.adaptToOldInterface(), file_size) catch |err| { | |
| 863 | 865 | const filename_string_index = try self.diagnostics.putString(filename_utf8); |
| 864 | 866 | return self.addErrorDetailsAndFail(.{ |
| 865 | 867 | .err = .bmp_read_error, |
| ... | ... | @@ -921,18 +923,17 @@ pub const Compiler = struct { |
| 921 | 923 | |
| 922 | 924 | header.data_size = bmp_bytes_to_write; |
| 923 | 925 | try header.write(writer, self.errContext(node.id)); |
| 924 | try file.seekTo(bmp.file_header_len); | |
| 925 | const file_reader = file.deprecatedReader(); | |
| 926 | try writeResourceDataNoPadding(writer, file_reader, bitmap_info.dib_header_size); | |
| 926 | try file_reader.seekTo(bmp.file_header_len); | |
| 927 | try writeResourceDataNoPadding(writer, &file_reader.interface, bitmap_info.dib_header_size); | |
| 927 | 928 | if (bitmap_info.getBitmasksByteLen() > 0) { |
| 928 | try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen()); | |
| 929 | try writeResourceDataNoPadding(writer, &file_reader.interface, bitmap_info.getBitmasksByteLen()); | |
| 929 | 930 | } |
| 930 | 931 | if (bitmap_info.getExpectedPaletteByteLen() > 0) { |
| 931 | try writeResourceDataNoPadding(writer, file_reader, @intCast(bitmap_info.getActualPaletteByteLen())); | |
| 932 | try writeResourceDataNoPadding(writer, &file_reader.interface, @intCast(bitmap_info.getActualPaletteByteLen())); | |
| 932 | 933 | } |
| 933 | try file.seekTo(bitmap_info.pixel_data_offset); | |
| 934 | try file_reader.seekTo(bitmap_info.pixel_data_offset); | |
| 934 | 935 | const pixel_bytes: u32 = @intCast(file_size - bitmap_info.pixel_data_offset); |
| 935 | try writeResourceDataNoPadding(writer, file_reader, pixel_bytes); | |
| 936 | try writeResourceDataNoPadding(writer, &file_reader.interface, pixel_bytes); | |
| 936 | 937 | try writeDataPadding(writer, bmp_bytes_to_write); |
| 937 | 938 | return; |
| 938 | 939 | }, |
| ... | ... | @@ -956,7 +957,7 @@ pub const Compiler = struct { |
| 956 | 957 | return; |
| 957 | 958 | } |
| 958 | 959 | header.applyMemoryFlags(node.common_resource_attributes, self.source); |
| 959 | const file_size = try file.getEndPos(); | |
| 960 | const file_size = try file_reader.getSize(); | |
| 960 | 961 | if (file_size > std.math.maxInt(u32)) { |
| 961 | 962 | return self.addErrorDetailsAndFail(.{ |
| 962 | 963 | .err = .resource_data_size_exceeds_max, |
| ... | ... | @@ -968,8 +969,9 @@ pub const Compiler = struct { |
| 968 | 969 | header.data_size = @intCast(file_size); |
| 969 | 970 | try header.write(writer, self.errContext(node.id)); |
| 970 | 971 | |
| 971 | var header_slurping_reader = headerSlurpingReader(148, file.deprecatedReader()); | |
| 972 | try writeResourceData(writer, header_slurping_reader.reader(), header.data_size); | |
| 972 | var header_slurping_reader = headerSlurpingReader(148, file_reader.interface.adaptToOldInterface()); | |
| 973 | var adapter = header_slurping_reader.reader().adaptToNewApi(&.{}); | |
| 974 | try writeResourceData(writer, &adapter.new_interface, header.data_size); | |
| 973 | 975 | |
| 974 | 976 | try self.state.font_dir.add(self.arena, FontDir.Font{ |
| 975 | 977 | .id = header.name_value.ordinal, |
| ... | ... | @@ -992,7 +994,7 @@ pub const Compiler = struct { |
| 992 | 994 | } |
| 993 | 995 | |
| 994 | 996 | // Fallback to just writing out the entire contents of the file |
| 995 | const data_size = try file.getEndPos(); | |
| 997 | const data_size = try file_reader.getSize(); | |
| 996 | 998 | if (data_size > std.math.maxInt(u32)) { |
| 997 | 999 | return self.addErrorDetailsAndFail(.{ |
| 998 | 1000 | .err = .resource_data_size_exceeds_max, |
| ... | ... | @@ -1002,7 +1004,7 @@ pub const Compiler = struct { |
| 1002 | 1004 | // We now know that the data size will fit in a u32 |
| 1003 | 1005 | header.data_size = @intCast(data_size); |
| 1004 | 1006 | try header.write(writer, self.errContext(node.id)); |
| 1005 | try writeResourceData(writer, file.deprecatedReader(), header.data_size); | |
| 1007 | try writeResourceData(writer, &file_reader.interface, header.data_size); | |
| 1006 | 1008 | } |
| 1007 | 1009 | |
| 1008 | 1010 | fn iconReadError( |
| ... | ... | @@ -1250,8 +1252,8 @@ pub const Compiler = struct { |
| 1250 | 1252 | const data_len: u32 = @intCast(data_buffer.items.len); |
| 1251 | 1253 | try self.writeResourceHeader(writer, node.id, node.type, data_len, node.common_resource_attributes, self.state.language); |
| 1252 | 1254 | |
| 1253 | var data_fbs = std.io.fixedBufferStream(data_buffer.items); | |
| 1254 | try writeResourceData(writer, data_fbs.reader(), data_len); | |
| 1255 | var data_fbs: std.Io.Reader = .fixed(data_buffer.items); | |
| 1256 | try writeResourceData(writer, &data_fbs, data_len); | |
| 1255 | 1257 | } |
| 1256 | 1258 | |
| 1257 | 1259 | pub fn writeResourceHeader(self: *Compiler, writer: anytype, id_token: Token, type_token: Token, data_size: u32, common_resource_attributes: []Token, language: res.Language) !void { |
| ... | ... | @@ -1266,13 +1268,15 @@ pub const Compiler = struct { |
| 1266 | 1268 | try header.write(writer, self.errContext(id_token)); |
| 1267 | 1269 | } |
| 1268 | 1270 | |
| 1269 | pub fn writeResourceDataNoPadding(writer: anytype, data_reader: anytype, data_size: u32) !void { | |
| 1270 | var limited_reader = std.io.limitedReader(data_reader, data_size); | |
| 1271 | ||
| 1272 | try limited_reader.reader().readRemaining(writer); | |
| 1271 | pub fn writeResourceDataNoPadding(writer: anytype, data_reader: *std.Io.Reader, data_size: u32) !void { | |
| 1272 | var adapted = writer.adaptToNewApi(); | |
| 1273 | var buffer: [128]u8 = undefined; | |
| 1274 | adapted.new_interface.buffer = &buffer; | |
| 1275 | try data_reader.streamExact(&adapted.new_interface, data_size); | |
| 1276 | try adapted.new_interface.flush(); | |
| 1273 | 1277 | } |
| 1274 | 1278 | |
| 1275 | pub fn writeResourceData(writer: anytype, data_reader: anytype, data_size: u32) !void { | |
| 1279 | pub fn writeResourceData(writer: anytype, data_reader: *std.Io.Reader, data_size: u32) !void { | |
| 1276 | 1280 | try writeResourceDataNoPadding(writer, data_reader, data_size); |
| 1277 | 1281 | try writeDataPadding(writer, data_size); |
| 1278 | 1282 | } |
| ... | ... | @@ -1337,8 +1341,8 @@ pub const Compiler = struct { |
| 1337 | 1341 | |
| 1338 | 1342 | try header.write(writer, self.errContext(node.id)); |
| 1339 | 1343 | |
| 1340 | var data_fbs = std.io.fixedBufferStream(data_buffer.items); | |
| 1341 | try writeResourceData(writer, data_fbs.reader(), data_size); | |
| 1344 | var data_fbs: std.Io.Reader = .fixed(data_buffer.items); | |
| 1345 | try writeResourceData(writer, &data_fbs, data_size); | |
| 1342 | 1346 | } |
| 1343 | 1347 | |
| 1344 | 1348 | /// Expects `data_writer` to be a LimitedWriter limited to u32, meaning all writes to |
| ... | ... | @@ -1730,8 +1734,8 @@ pub const Compiler = struct { |
| 1730 | 1734 | |
| 1731 | 1735 | try header.write(writer, self.errContext(node.id)); |
| 1732 | 1736 | |
| 1733 | var data_fbs = std.io.fixedBufferStream(data_buffer.items); | |
| 1734 | try writeResourceData(writer, data_fbs.reader(), data_size); | |
| 1737 | var data_fbs: std.Io.Reader = .fixed(data_buffer.items); | |
| 1738 | try writeResourceData(writer, &data_fbs, data_size); | |
| 1735 | 1739 | } |
| 1736 | 1740 | |
| 1737 | 1741 | fn writeDialogHeaderAndStrings( |
| ... | ... | @@ -2044,8 +2048,8 @@ pub const Compiler = struct { |
| 2044 | 2048 | |
| 2045 | 2049 | try header.write(writer, self.errContext(node.id)); |
| 2046 | 2050 | |
| 2047 | var data_fbs = std.io.fixedBufferStream(data_buffer.items); | |
| 2048 | try writeResourceData(writer, data_fbs.reader(), data_size); | |
| 2051 | var data_fbs: std.Io.Reader = .fixed(data_buffer.items); | |
| 2052 | try writeResourceData(writer, &data_fbs, data_size); | |
| 2049 | 2053 | } |
| 2050 | 2054 | |
| 2051 | 2055 | /// Weight and italic carry over from previous FONT statements within a single resource, |
| ... | ... | @@ -2119,8 +2123,8 @@ pub const Compiler = struct { |
| 2119 | 2123 | |
| 2120 | 2124 | try header.write(writer, self.errContext(node.id)); |
| 2121 | 2125 | |
| 2122 | var data_fbs = std.io.fixedBufferStream(data_buffer.items); | |
| 2123 | try writeResourceData(writer, data_fbs.reader(), data_size); | |
| 2126 | var data_fbs: std.Io.Reader = .fixed(data_buffer.items); | |
| 2127 | try writeResourceData(writer, &data_fbs, data_size); | |
| 2124 | 2128 | } |
| 2125 | 2129 | |
| 2126 | 2130 | /// Expects `data_writer` to be a LimitedWriter limited to u32, meaning all writes to |
| ... | ... | @@ -2384,8 +2388,8 @@ pub const Compiler = struct { |
| 2384 | 2388 | |
| 2385 | 2389 | try header.write(writer, self.errContext(node.id)); |
| 2386 | 2390 | |
| 2387 | var data_fbs = std.io.fixedBufferStream(data_buffer.items); | |
| 2388 | try writeResourceData(writer, data_fbs.reader(), data_size); | |
| 2391 | var data_fbs: std.Io.Reader = .fixed(data_buffer.items); | |
| 2392 | try writeResourceData(writer, &data_fbs, data_size); | |
| 2389 | 2393 | } |
| 2390 | 2394 | |
| 2391 | 2395 | /// Expects writer to be a LimitedWriter limited to u16, meaning all writes to |
| ... | ... | @@ -3319,8 +3323,8 @@ pub const StringTable = struct { |
| 3319 | 3323 | // we fully control and know are numbers, so they have a fixed size. |
| 3320 | 3324 | try header.writeAssertNoOverflow(writer); |
| 3321 | 3325 | |
| 3322 | var data_fbs = std.io.fixedBufferStream(data_buffer.items); | |
| 3323 | try Compiler.writeResourceData(writer, data_fbs.reader(), data_size); | |
| 3326 | var data_fbs: std.Io.Reader = .fixed(data_buffer.items); | |
| 3327 | try Compiler.writeResourceData(writer, &data_fbs, data_size); | |
| 3324 | 3328 | } |
| 3325 | 3329 | }; |
| 3326 | 3330 |
lib/compiler/resinator/cvtres.zig+24-29| ... | ... | @@ -65,7 +65,7 @@ pub const ParseResOptions = struct { |
| 65 | 65 | }; |
| 66 | 66 | |
| 67 | 67 | /// The returned ParsedResources should be freed by calling its `deinit` function. |
| 68 | pub fn parseRes(allocator: Allocator, reader: anytype, options: ParseResOptions) !ParsedResources { | |
| 68 | pub fn parseRes(allocator: Allocator, reader: *std.Io.Reader, options: ParseResOptions) !ParsedResources { | |
| 69 | 69 | var resources = ParsedResources.init(allocator); |
| 70 | 70 | errdefer resources.deinit(); |
| 71 | 71 | |
| ... | ... | @@ -74,7 +74,7 @@ pub fn parseRes(allocator: Allocator, reader: anytype, options: ParseResOptions) |
| 74 | 74 | return resources; |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | pub fn parseResInto(resources: *ParsedResources, reader: anytype, options: ParseResOptions) !void { | |
| 77 | pub fn parseResInto(resources: *ParsedResources, reader: *std.Io.Reader, options: ParseResOptions) !void { | |
| 78 | 78 | const allocator = resources.allocator; |
| 79 | 79 | var bytes_remaining: u64 = options.max_size; |
| 80 | 80 | { |
| ... | ... | @@ -103,43 +103,38 @@ pub const ResourceAndSize = struct { |
| 103 | 103 | total_size: u64, |
| 104 | 104 | }; |
| 105 | 105 | |
| 106 | pub fn parseResource(allocator: Allocator, reader: anytype, max_size: u64) !ResourceAndSize { | |
| 107 | var header_counting_reader = std.io.countingReader(reader); | |
| 108 | const header_reader = header_counting_reader.reader(); | |
| 109 | const data_size = try header_reader.readInt(u32, .little); | |
| 110 | const header_size = try header_reader.readInt(u32, .little); | |
| 106 | pub fn parseResource(allocator: Allocator, reader: *std.Io.Reader, max_size: u64) !ResourceAndSize { | |
| 107 | const data_size = try reader.takeInt(u32, .little); | |
| 108 | const header_size = try reader.takeInt(u32, .little); | |
| 111 | 109 | const total_size: u64 = @as(u64, header_size) + data_size; |
| 112 | 110 | if (total_size > max_size) return error.ImpossibleSize; |
| 113 | 111 | |
| 114 | var header_bytes_available = header_size -| 8; | |
| 115 | var type_reader = std.io.limitedReader(header_reader, header_bytes_available); | |
| 116 | const type_value = try parseNameOrOrdinal(allocator, type_reader.reader()); | |
| 112 | const remaining_header_bytes = try reader.take(header_size -| 8); | |
| 113 | var remaining_header_reader: std.Io.Reader = .fixed(remaining_header_bytes); | |
| 114 | const type_value = try parseNameOrOrdinal(allocator, &remaining_header_reader); | |
| 117 | 115 | errdefer type_value.deinit(allocator); |
| 118 | 116 | |
| 119 | header_bytes_available -|= @intCast(type_value.byteLen()); | |
| 120 | var name_reader = std.io.limitedReader(header_reader, header_bytes_available); | |
| 121 | const name_value = try parseNameOrOrdinal(allocator, name_reader.reader()); | |
| 117 | const name_value = try parseNameOrOrdinal(allocator, &remaining_header_reader); | |
| 122 | 118 | errdefer name_value.deinit(allocator); |
| 123 | 119 | |
| 124 | const padding_after_name = numPaddingBytesNeeded(@intCast(header_counting_reader.bytes_read)); | |
| 125 | try header_reader.skipBytes(padding_after_name, .{ .buf_size = 3 }); | |
| 120 | const padding_after_name = numPaddingBytesNeeded(@intCast(remaining_header_reader.seek)); | |
| 121 | try remaining_header_reader.discardAll(padding_after_name); | |
| 126 | 122 | |
| 127 | std.debug.assert(header_counting_reader.bytes_read % 4 == 0); | |
| 128 | const data_version = try header_reader.readInt(u32, .little); | |
| 129 | const memory_flags: MemoryFlags = @bitCast(try header_reader.readInt(u16, .little)); | |
| 130 | const language: Language = @bitCast(try header_reader.readInt(u16, .little)); | |
| 131 | const version = try header_reader.readInt(u32, .little); | |
| 132 | const characteristics = try header_reader.readInt(u32, .little); | |
| 123 | std.debug.assert(remaining_header_reader.seek % 4 == 0); | |
| 124 | const data_version = try remaining_header_reader.takeInt(u32, .little); | |
| 125 | const memory_flags: MemoryFlags = @bitCast(try remaining_header_reader.takeInt(u16, .little)); | |
| 126 | const language: Language = @bitCast(try remaining_header_reader.takeInt(u16, .little)); | |
| 127 | const version = try remaining_header_reader.takeInt(u32, .little); | |
| 128 | const characteristics = try remaining_header_reader.takeInt(u32, .little); | |
| 133 | 129 | |
| 134 | const header_bytes_read = header_counting_reader.bytes_read; | |
| 135 | if (header_size != header_bytes_read) return error.HeaderSizeMismatch; | |
| 130 | if (remaining_header_reader.seek != remaining_header_reader.end) return error.HeaderSizeMismatch; | |
| 136 | 131 | |
| 137 | 132 | const data = try allocator.alloc(u8, data_size); |
| 138 | 133 | errdefer allocator.free(data); |
| 139 | try reader.readNoEof(data); | |
| 134 | try reader.readSliceAll(data); | |
| 140 | 135 | |
| 141 | 136 | const padding_after_data = numPaddingBytesNeeded(@intCast(data_size)); |
| 142 | try reader.skipBytes(padding_after_data, .{ .buf_size = 3 }); | |
| 137 | try reader.discardAll(padding_after_data); | |
| 143 | 138 | |
| 144 | 139 | return .{ |
| 145 | 140 | .resource = .{ |
| ... | ... | @@ -156,10 +151,10 @@ pub fn parseResource(allocator: Allocator, reader: anytype, max_size: u64) !Reso |
| 156 | 151 | }; |
| 157 | 152 | } |
| 158 | 153 | |
| 159 | pub fn parseNameOrOrdinal(allocator: Allocator, reader: anytype) !NameOrOrdinal { | |
| 160 | const first_code_unit = try reader.readInt(u16, .little); | |
| 154 | pub fn parseNameOrOrdinal(allocator: Allocator, reader: *std.Io.Reader) !NameOrOrdinal { | |
| 155 | const first_code_unit = try reader.takeInt(u16, .little); | |
| 161 | 156 | if (first_code_unit == 0xFFFF) { |
| 162 | const ordinal_value = try reader.readInt(u16, .little); | |
| 157 | const ordinal_value = try reader.takeInt(u16, .little); | |
| 163 | 158 | return .{ .ordinal = ordinal_value }; |
| 164 | 159 | } |
| 165 | 160 | var name_buf = try std.ArrayListUnmanaged(u16).initCapacity(allocator, 16); |
| ... | ... | @@ -167,7 +162,7 @@ pub fn parseNameOrOrdinal(allocator: Allocator, reader: anytype) !NameOrOrdinal |
| 167 | 162 | var code_unit = first_code_unit; |
| 168 | 163 | while (code_unit != 0) { |
| 169 | 164 | try name_buf.append(allocator, std.mem.nativeToLittle(u16, code_unit)); |
| 170 | code_unit = try reader.readInt(u16, .little); | |
| 165 | code_unit = try reader.takeInt(u16, .little); | |
| 171 | 166 | } |
| 172 | 167 | return .{ .name = try name_buf.toOwnedSliceSentinel(allocator, 0) }; |
| 173 | 168 | } |
lib/compiler/resinator/errors.zig+2-2| ... | ... | @@ -1112,7 +1112,7 @@ const CorrespondingLines = struct { |
| 1112 | 1112 | |
| 1113 | 1113 | try corresponding_lines.writeLineFromStreamVerbatim( |
| 1114 | 1114 | writer, |
| 1115 | corresponding_lines.buffered_reader.reader(), | |
| 1115 | corresponding_lines.buffered_reader.interface.adaptToOldInterface(), | |
| 1116 | 1116 | corresponding_span.start_line, |
| 1117 | 1117 | ); |
| 1118 | 1118 | |
| ... | ... | @@ -1155,7 +1155,7 @@ const CorrespondingLines = struct { |
| 1155 | 1155 | |
| 1156 | 1156 | try self.writeLineFromStreamVerbatim( |
| 1157 | 1157 | writer, |
| 1158 | self.buffered_reader.reader(), | |
| 1158 | self.buffered_reader.interface.adaptToOldInterface(), | |
| 1159 | 1159 | self.line_num, |
| 1160 | 1160 | ); |
| 1161 | 1161 |
lib/compiler/resinator/ico.zig+2-1| ... | ... | @@ -14,8 +14,9 @@ pub fn read(allocator: std.mem.Allocator, reader: anytype, max_size: u64) ReadEr |
| 14 | 14 | // Some Reader implementations have an empty ReadError error set which would |
| 15 | 15 | // cause 'unreachable else' if we tried to use an else in the switch, so we |
| 16 | 16 | // need to detect this case and not try to translate to ReadError |
| 17 | const anyerror_reader_errorset = @TypeOf(reader).Error == anyerror; | |
| 17 | 18 | const empty_reader_errorset = @typeInfo(@TypeOf(reader).Error).error_set == null or @typeInfo(@TypeOf(reader).Error).error_set.?.len == 0; |
| 18 | if (empty_reader_errorset) { | |
| 19 | if (empty_reader_errorset and !anyerror_reader_errorset) { | |
| 19 | 20 | return readAnyError(allocator, reader, max_size) catch |err| switch (err) { |
| 20 | 21 | error.EndOfStream => error.UnexpectedEOF, |
| 21 | 22 | else => |e| return e, |
lib/compiler/resinator/main.zig+2-2| ... | ... | @@ -325,8 +325,8 @@ pub fn main() !void { |
| 325 | 325 | std.debug.assert(options.output_format == .coff); |
| 326 | 326 | |
| 327 | 327 | // TODO: Maybe use a buffered file reader instead of reading file into memory -> fbs |
| 328 | var fbs = std.io.fixedBufferStream(res_data.bytes); | |
| 329 | break :resources cvtres.parseRes(allocator, fbs.reader(), .{ .max_size = res_data.bytes.len }) catch |err| { | |
| 328 | var res_reader: std.Io.Reader = .fixed(res_data.bytes); | |
| 329 | break :resources cvtres.parseRes(allocator, &res_reader, .{ .max_size = res_data.bytes.len }) catch |err| { | |
| 330 | 330 | // TODO: Better errors |
| 331 | 331 | try error_handler.emitMessage(allocator, .err, "unable to parse res from '{s}': {s}", .{ res_stream.name, @errorName(err) }); |
| 332 | 332 | std.process.exit(1); |
lib/docs/main.js+13-3| ... | ... | @@ -129,6 +129,11 @@ |
| 129 | 129 | domSearch.addEventListener('input', onSearchChange, false); |
| 130 | 130 | window.addEventListener('keydown', onWindowKeyDown, false); |
| 131 | 131 | onHashChange(null); |
| 132 | if (domSearch.value) { | |
| 133 | // user started typing a search query while the page was loading | |
| 134 | curSearchIndex = -1; | |
| 135 | startAsyncSearch(); | |
| 136 | } | |
| 132 | 137 | }); |
| 133 | 138 | }); |
| 134 | 139 | |
| ... | ... | @@ -643,6 +648,7 @@ |
| 643 | 648 | } |
| 644 | 649 | |
| 645 | 650 | function onHashChange(state) { |
| 651 | // Use a non-null state value to prevent the window scrolling if the user goes back to this history entry. | |
| 646 | 652 | history.replaceState({}, ""); |
| 647 | 653 | navigate(location.hash); |
| 648 | 654 | if (state == null) window.scrollTo({top: 0}); |
| ... | ... | @@ -650,13 +656,11 @@ |
| 650 | 656 | |
| 651 | 657 | function onPopState(ev) { |
| 652 | 658 | onHashChange(ev.state); |
| 659 | syncDomSearch(); | |
| 653 | 660 | } |
| 654 | 661 | |
| 655 | 662 | function navigate(location_hash) { |
| 656 | 663 | updateCurNav(location_hash); |
| 657 | if (domSearch.value !== curNavSearch) { | |
| 658 | domSearch.value = curNavSearch; | |
| 659 | } | |
| 660 | 664 | render(); |
| 661 | 665 | if (imFeelingLucky) { |
| 662 | 666 | imFeelingLucky = false; |
| ... | ... | @@ -664,6 +668,12 @@ |
| 664 | 668 | } |
| 665 | 669 | } |
| 666 | 670 | |
| 671 | function syncDomSearch() { | |
| 672 | if (domSearch.value !== curNavSearch) { | |
| 673 | domSearch.value = curNavSearch; | |
| 674 | } | |
| 675 | } | |
| 676 | ||
| 667 | 677 | function activateSelectedResult() { |
| 668 | 678 | if (domSectSearchResults.classList.contains("hidden")) { |
| 669 | 679 | return; |
lib/docs/wasm/markdown.zig+6-7| ... | ... | @@ -143,13 +143,12 @@ fn mainImpl() !void { |
| 143 | 143 | var parser = try Parser.init(gpa); |
| 144 | 144 | defer parser.deinit(); |
| 145 | 145 | |
| 146 | var stdin_buf = std.io.bufferedReader(std.fs.File.stdin().deprecatedReader()); | |
| 147 | var line_buf = std.ArrayList(u8).init(gpa); | |
| 148 | defer line_buf.deinit(); | |
| 149 | while (stdin_buf.reader().streamUntilDelimiter(line_buf.writer(), '\n', null)) { | |
| 150 | if (line_buf.getLastOrNull() == '\r') _ = line_buf.pop(); | |
| 151 | try parser.feedLine(line_buf.items); | |
| 152 | line_buf.clearRetainingCapacity(); | |
| 146 | var stdin_buffer: [1024]u8 = undefined; | |
| 147 | var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer); | |
| 148 | ||
| 149 | while (stdin_reader.takeDelimiterExclusive('\n')) |line| { | |
| 150 | const trimmed = std.mem.trimRight(u8, line, '\r'); | |
| 151 | try parser.feedLine(trimmed); | |
| 153 | 152 | } else |err| switch (err) { |
| 154 | 153 | error.EndOfStream => {}, |
| 155 | 154 | else => |e| return e, |
lib/docs/wasm/markdown/Parser.zig+40-26| ... | ... | @@ -29,13 +29,14 @@ const Node = Document.Node; |
| 29 | 29 | const ExtraIndex = Document.ExtraIndex; |
| 30 | 30 | const ExtraData = Document.ExtraData; |
| 31 | 31 | const StringIndex = Document.StringIndex; |
| 32 | const ArrayList = std.ArrayListUnmanaged; | |
| 32 | 33 | |
| 33 | 34 | nodes: Node.List = .{}, |
| 34 | extra: std.ArrayListUnmanaged(u32) = .empty, | |
| 35 | scratch_extra: std.ArrayListUnmanaged(u32) = .empty, | |
| 36 | string_bytes: std.ArrayListUnmanaged(u8) = .empty, | |
| 37 | scratch_string: std.ArrayListUnmanaged(u8) = .empty, | |
| 38 | pending_blocks: std.ArrayListUnmanaged(Block) = .empty, | |
| 35 | extra: ArrayList(u32) = .empty, | |
| 36 | scratch_extra: ArrayList(u32) = .empty, | |
| 37 | string_bytes: ArrayList(u8) = .empty, | |
| 38 | scratch_string: ArrayList(u8) = .empty, | |
| 39 | pending_blocks: ArrayList(Block) = .empty, | |
| 39 | 40 | allocator: Allocator, |
| 40 | 41 | |
| 41 | 42 | const Parser = @This(); |
| ... | ... | @@ -86,7 +87,8 @@ const Block = struct { |
| 86 | 87 | continuation_indent: usize, |
| 87 | 88 | }, |
| 88 | 89 | table: struct { |
| 89 | column_alignments: std.BoundedArray(Node.TableCellAlignment, max_table_columns) = .{}, | |
| 90 | column_alignments_buffer: [max_table_columns]Node.TableCellAlignment, | |
| 91 | column_alignments_len: usize, | |
| 90 | 92 | }, |
| 91 | 93 | heading: struct { |
| 92 | 94 | /// Between 1 and 6, inclusive. |
| ... | ... | @@ -354,7 +356,8 @@ const BlockStart = struct { |
| 354 | 356 | continuation_indent: usize, |
| 355 | 357 | }, |
| 356 | 358 | table_row: struct { |
| 357 | cells: std.BoundedArray([]const u8, max_table_columns), | |
| 359 | cells_buffer: [max_table_columns][]const u8, | |
| 360 | cells_len: usize, | |
| 358 | 361 | }, |
| 359 | 362 | heading: struct { |
| 360 | 363 | /// Between 1 and 6, inclusive. |
| ... | ... | @@ -422,7 +425,8 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void { |
| 422 | 425 | try p.pending_blocks.append(p.allocator, .{ |
| 423 | 426 | .tag = .table, |
| 424 | 427 | .data = .{ .table = .{ |
| 425 | .column_alignments = .{}, | |
| 428 | .column_alignments_buffer = undefined, | |
| 429 | .column_alignments_len = 0, | |
| 426 | 430 | } }, |
| 427 | 431 | .string_start = p.scratch_string.items.len, |
| 428 | 432 | .extra_start = p.scratch_extra.items.len, |
| ... | ... | @@ -431,15 +435,19 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void { |
| 431 | 435 | |
| 432 | 436 | const current_row = p.scratch_extra.items.len - p.pending_blocks.getLast().extra_start; |
| 433 | 437 | if (current_row <= 1) { |
| 434 | if (parseTableHeaderDelimiter(block_start.data.table_row.cells)) |alignments| { | |
| 435 | p.pending_blocks.items[p.pending_blocks.items.len - 1].data.table.column_alignments = alignments; | |
| 438 | var buffer: [max_table_columns]Node.TableCellAlignment = undefined; | |
| 439 | const table_row = &block_start.data.table_row; | |
| 440 | if (parseTableHeaderDelimiter(table_row.cells_buffer[0..table_row.cells_len], &buffer)) |alignments| { | |
| 441 | const table = &p.pending_blocks.items[p.pending_blocks.items.len - 1].data.table; | |
| 442 | @memcpy(table.column_alignments_buffer[0..alignments.len], alignments); | |
| 443 | table.column_alignments_len = alignments.len; | |
| 436 | 444 | if (current_row == 1) { |
| 437 | 445 | // We need to go back and mark the header row and its column |
| 438 | 446 | // alignments. |
| 439 | 447 | const datas = p.nodes.items(.data); |
| 440 | 448 | const header_data = datas[p.scratch_extra.getLast()]; |
| 441 | 449 | for (p.extraChildren(header_data.container.children), 0..) |header_cell, i| { |
| 442 | const alignment = if (i < alignments.len) alignments.buffer[i] else .unset; | |
| 450 | const alignment = if (i < alignments.len) alignments[i] else .unset; | |
| 443 | 451 | const cell_data = &datas[@intFromEnum(header_cell)].table_cell; |
| 444 | 452 | cell_data.info.alignment = alignment; |
| 445 | 453 | cell_data.info.header = true; |
| ... | ... | @@ -480,8 +488,10 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void { |
| 480 | 488 | // available in the BlockStart. We can immediately parse and append |
| 481 | 489 | // these children now. |
| 482 | 490 | const containing_table = p.pending_blocks.items[p.pending_blocks.items.len - 2]; |
| 483 | const column_alignments = containing_table.data.table.column_alignments.slice(); | |
| 484 | for (block_start.data.table_row.cells.slice(), 0..) |cell_content, i| { | |
| 491 | const table = &containing_table.data.table; | |
| 492 | const column_alignments = table.column_alignments_buffer[0..table.column_alignments_len]; | |
| 493 | const table_row = &block_start.data.table_row; | |
| 494 | for (table_row.cells_buffer[0..table_row.cells_len], 0..) |cell_content, i| { | |
| 485 | 495 | const cell_children = try p.parseInlines(cell_content); |
| 486 | 496 | const alignment = if (i < column_alignments.len) column_alignments[i] else .unset; |
| 487 | 497 | const cell = try p.addNode(.{ |
| ... | ... | @@ -523,7 +533,8 @@ fn startBlock(p: *Parser, line: []const u8) !?BlockStart { |
| 523 | 533 | return .{ |
| 524 | 534 | .tag = .table_row, |
| 525 | 535 | .data = .{ .table_row = .{ |
| 526 | .cells = table_row.cells, | |
| 536 | .cells_buffer = table_row.cells_buffer, | |
| 537 | .cells_len = table_row.cells_len, | |
| 527 | 538 | } }, |
| 528 | 539 | .rest = "", |
| 529 | 540 | }; |
| ... | ... | @@ -606,7 +617,8 @@ fn startListItem(unindented_line: []const u8) ?ListItemStart { |
| 606 | 617 | } |
| 607 | 618 | |
| 608 | 619 | const TableRowStart = struct { |
| 609 | cells: std.BoundedArray([]const u8, max_table_columns), | |
| 620 | cells_buffer: [max_table_columns][]const u8, | |
| 621 | cells_len: usize, | |
| 610 | 622 | }; |
| 611 | 623 | |
| 612 | 624 | fn startTableRow(unindented_line: []const u8) ?TableRowStart { |
| ... | ... | @@ -615,7 +627,8 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart { |
| 615 | 627 | mem.endsWith(u8, unindented_line, "\\|") or |
| 616 | 628 | !mem.endsWith(u8, unindented_line, "|")) return null; |
| 617 | 629 | |
| 618 | var cells: std.BoundedArray([]const u8, max_table_columns) = .{}; | |
| 630 | var cells_buffer: [max_table_columns][]const u8 = undefined; | |
| 631 | var cells: ArrayList([]const u8) = .initBuffer(&cells_buffer); | |
| 619 | 632 | const table_row_content = unindented_line[1 .. unindented_line.len - 1]; |
| 620 | 633 | var cell_start: usize = 0; |
| 621 | 634 | var i: usize = 0; |
| ... | ... | @@ -623,7 +636,7 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart { |
| 623 | 636 | switch (table_row_content[i]) { |
| 624 | 637 | '\\' => i += 1, |
| 625 | 638 | '|' => { |
| 626 | cells.append(table_row_content[cell_start..i]) catch return null; | |
| 639 | cells.appendBounded(table_row_content[cell_start..i]) catch return null; | |
| 627 | 640 | cell_start = i + 1; |
| 628 | 641 | }, |
| 629 | 642 | '`' => { |
| ... | ... | @@ -641,20 +654,21 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart { |
| 641 | 654 | else => {}, |
| 642 | 655 | } |
| 643 | 656 | } |
| 644 | cells.append(table_row_content[cell_start..]) catch return null; | |
| 657 | cells.appendBounded(table_row_content[cell_start..]) catch return null; | |
| 645 | 658 | |
| 646 | return .{ .cells = cells }; | |
| 659 | return .{ .cells_buffer = cells_buffer, .cells_len = cells.items.len }; | |
| 647 | 660 | } |
| 648 | 661 | |
| 649 | 662 | fn parseTableHeaderDelimiter( |
| 650 | row_cells: std.BoundedArray([]const u8, max_table_columns), | |
| 651 | ) ?std.BoundedArray(Node.TableCellAlignment, max_table_columns) { | |
| 652 | var alignments: std.BoundedArray(Node.TableCellAlignment, max_table_columns) = .{}; | |
| 653 | for (row_cells.slice()) |content| { | |
| 663 | row_cells: []const []const u8, | |
| 664 | buffer: []Node.TableCellAlignment, | |
| 665 | ) ?[]Node.TableCellAlignment { | |
| 666 | var alignments: ArrayList(Node.TableCellAlignment) = .initBuffer(buffer); | |
| 667 | for (row_cells) |content| { | |
| 654 | 668 | const alignment = parseTableHeaderDelimiterCell(content) orelse return null; |
| 655 | 669 | alignments.appendAssumeCapacity(alignment); |
| 656 | 670 | } |
| 657 | return alignments; | |
| 671 | return alignments.items; | |
| 658 | 672 | } |
| 659 | 673 | |
| 660 | 674 | fn parseTableHeaderDelimiterCell(content: []const u8) ?Node.TableCellAlignment { |
| ... | ... | @@ -928,8 +942,8 @@ const InlineParser = struct { |
| 928 | 942 | parent: *Parser, |
| 929 | 943 | content: []const u8, |
| 930 | 944 | pos: usize = 0, |
| 931 | pending_inlines: std.ArrayListUnmanaged(PendingInline) = .empty, | |
| 932 | completed_inlines: std.ArrayListUnmanaged(CompletedInline) = .empty, | |
| 945 | pending_inlines: ArrayList(PendingInline) = .empty, | |
| 946 | completed_inlines: ArrayList(CompletedInline) = .empty, | |
| 933 | 947 | |
| 934 | 948 | const PendingInline = struct { |
| 935 | 949 | tag: Tag, |
lib/std/Build/Fuzz.zig+16-23| ... | ... | @@ -234,7 +234,7 @@ pub const Previous = struct { |
| 234 | 234 | }; |
| 235 | 235 | pub fn sendUpdate( |
| 236 | 236 | fuzz: *Fuzz, |
| 237 | socket: *std.http.WebSocket, | |
| 237 | socket: *std.http.Server.WebSocket, | |
| 238 | 238 | prev: *Previous, |
| 239 | 239 | ) !void { |
| 240 | 240 | fuzz.coverage_mutex.lock(); |
| ... | ... | @@ -263,36 +263,36 @@ pub fn sendUpdate( |
| 263 | 263 | .string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len), |
| 264 | 264 | .start_timestamp = coverage_map.start_timestamp, |
| 265 | 265 | }; |
| 266 | const iovecs: [5]std.posix.iovec_const = .{ | |
| 267 | makeIov(@ptrCast(&header)), | |
| 268 | makeIov(@ptrCast(coverage_map.coverage.directories.keys())), | |
| 269 | makeIov(@ptrCast(coverage_map.coverage.files.keys())), | |
| 270 | makeIov(@ptrCast(coverage_map.source_locations)), | |
| 271 | makeIov(coverage_map.coverage.string_bytes.items), | |
| 266 | var iovecs: [5][]const u8 = .{ | |
| 267 | @ptrCast(&header), | |
| 268 | @ptrCast(coverage_map.coverage.directories.keys()), | |
| 269 | @ptrCast(coverage_map.coverage.files.keys()), | |
| 270 | @ptrCast(coverage_map.source_locations), | |
| 271 | coverage_map.coverage.string_bytes.items, | |
| 272 | 272 | }; |
| 273 | try socket.writeMessagev(&iovecs, .binary); | |
| 273 | try socket.writeMessageVec(&iovecs, .binary); | |
| 274 | 274 | } |
| 275 | 275 | |
| 276 | 276 | const header: abi.CoverageUpdateHeader = .{ |
| 277 | 277 | .n_runs = n_runs, |
| 278 | 278 | .unique_runs = unique_runs, |
| 279 | 279 | }; |
| 280 | const iovecs: [2]std.posix.iovec_const = .{ | |
| 281 | makeIov(@ptrCast(&header)), | |
| 282 | makeIov(@ptrCast(seen_pcs)), | |
| 280 | var iovecs: [2][]const u8 = .{ | |
| 281 | @ptrCast(&header), | |
| 282 | @ptrCast(seen_pcs), | |
| 283 | 283 | }; |
| 284 | try socket.writeMessagev(&iovecs, .binary); | |
| 284 | try socket.writeMessageVec(&iovecs, .binary); | |
| 285 | 285 | |
| 286 | 286 | prev.unique_runs = unique_runs; |
| 287 | 287 | } |
| 288 | 288 | |
| 289 | 289 | if (prev.entry_points != coverage_map.entry_points.items.len) { |
| 290 | 290 | const header: abi.EntryPointHeader = .init(@intCast(coverage_map.entry_points.items.len)); |
| 291 | const iovecs: [2]std.posix.iovec_const = .{ | |
| 292 | makeIov(@ptrCast(&header)), | |
| 293 | makeIov(@ptrCast(coverage_map.entry_points.items)), | |
| 291 | var iovecs: [2][]const u8 = .{ | |
| 292 | @ptrCast(&header), | |
| 293 | @ptrCast(coverage_map.entry_points.items), | |
| 294 | 294 | }; |
| 295 | try socket.writeMessagev(&iovecs, .binary); | |
| 295 | try socket.writeMessageVec(&iovecs, .binary); | |
| 296 | 296 | |
| 297 | 297 | prev.entry_points = coverage_map.entry_points.items.len; |
| 298 | 298 | } |
| ... | ... | @@ -448,10 +448,3 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte |
| 448 | 448 | } |
| 449 | 449 | try coverage_map.entry_points.append(fuzz.ws.gpa, @intCast(index)); |
| 450 | 450 | } |
| 451 | ||
| 452 | fn makeIov(s: []const u8) std.posix.iovec_const { | |
| 453 | return .{ | |
| 454 | .base = s.ptr, | |
| 455 | .len = s.len, | |
| 456 | }; | |
| 457 | } |
lib/std/Build/Step/Compile.zig+1-1| ... | ... | @@ -1851,7 +1851,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 1851 | 1851 | const maybe_output_dir = step.evalZigProcess( |
| 1852 | 1852 | zig_args, |
| 1853 | 1853 | options.progress_node, |
| 1854 | (b.graph.incremental == true) and options.watch, | |
| 1854 | (b.graph.incremental == true) and (options.watch or options.web_server != null), | |
| 1855 | 1855 | options.web_server, |
| 1856 | 1856 | options.gpa, |
| 1857 | 1857 | ) catch |err| switch (err) { |
lib/std/Build/Watch.zig+36-3| ... | ... | @@ -1,13 +1,18 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("../std.zig"); |
| 3 | const Watch = @This(); | |
| 4 | 3 | const Step = std.Build.Step; |
| 5 | 4 | const Allocator = std.mem.Allocator; |
| 6 | 5 | const assert = std.debug.assert; |
| 7 | 6 | const fatal = std.process.fatal; |
| 7 | const Watch = @This(); | |
| 8 | const FsEvents = @import("Watch/FsEvents.zig"); | |
| 8 | 9 | |
| 9 | dir_table: DirTable, | |
| 10 | 10 | os: Os, |
| 11 | /// The number to show as the number of directories being watched. | |
| 12 | dir_count: usize, | |
| 13 | // These fields are common to most implementations so are kept here for simplicity. | |
| 14 | // They are `undefined` on implementations which do not utilize then. | |
| 15 | dir_table: DirTable, | |
| 11 | 16 | generation: Generation, |
| 12 | 17 | |
| 13 | 18 | pub const have_impl = Os != void; |
| ... | ... | @@ -97,6 +102,7 @@ const Os = switch (builtin.os.tag) { |
| 97 | 102 | fn init() !Watch { |
| 98 | 103 | return .{ |
| 99 | 104 | .dir_table = .{}, |
| 105 | .dir_count = 0, | |
| 100 | 106 | .os = switch (builtin.os.tag) { |
| 101 | 107 | .linux => .{ |
| 102 | 108 | .handle_table = .{}, |
| ... | ... | @@ -273,6 +279,7 @@ const Os = switch (builtin.os.tag) { |
| 273 | 279 | } |
| 274 | 280 | w.generation +%= 1; |
| 275 | 281 | } |
| 282 | w.dir_count = w.dir_table.count(); | |
| 276 | 283 | } |
| 277 | 284 | |
| 278 | 285 | fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { |
| ... | ... | @@ -408,6 +415,7 @@ const Os = switch (builtin.os.tag) { |
| 408 | 415 | fn init() !Watch { |
| 409 | 416 | return .{ |
| 410 | 417 | .dir_table = .{}, |
| 418 | .dir_count = 0, | |
| 411 | 419 | .os = switch (builtin.os.tag) { |
| 412 | 420 | .windows => .{ |
| 413 | 421 | .handle_table = .{}, |
| ... | ... | @@ -572,6 +580,7 @@ const Os = switch (builtin.os.tag) { |
| 572 | 580 | } |
| 573 | 581 | w.generation +%= 1; |
| 574 | 582 | } |
| 583 | w.dir_count = w.dir_table.count(); | |
| 575 | 584 | } |
| 576 | 585 | |
| 577 | 586 | fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { |
| ... | ... | @@ -605,7 +614,7 @@ const Os = switch (builtin.os.tag) { |
| 605 | 614 | }; |
| 606 | 615 | } |
| 607 | 616 | }, |
| 608 | .dragonfly, .freebsd, .netbsd, .openbsd, .ios, .macos, .tvos, .visionos, .watchos => struct { | |
| 617 | .dragonfly, .freebsd, .netbsd, .openbsd, .ios, .tvos, .visionos, .watchos => struct { | |
| 609 | 618 | const posix = std.posix; |
| 610 | 619 | |
| 611 | 620 | kq_fd: i32, |
| ... | ... | @@ -639,6 +648,7 @@ const Os = switch (builtin.os.tag) { |
| 639 | 648 | errdefer posix.close(kq_fd); |
| 640 | 649 | return .{ |
| 641 | 650 | .dir_table = .{}, |
| 651 | .dir_count = 0, | |
| 642 | 652 | .os = .{ |
| 643 | 653 | .kq_fd = kq_fd, |
| 644 | 654 | .handles = .empty, |
| ... | ... | @@ -769,6 +779,7 @@ const Os = switch (builtin.os.tag) { |
| 769 | 779 | } |
| 770 | 780 | w.generation +%= 1; |
| 771 | 781 | } |
| 782 | w.dir_count = w.dir_table.count(); | |
| 772 | 783 | } |
| 773 | 784 | |
| 774 | 785 | fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { |
| ... | ... | @@ -812,6 +823,28 @@ const Os = switch (builtin.os.tag) { |
| 812 | 823 | return any_dirty; |
| 813 | 824 | } |
| 814 | 825 | }, |
| 826 | .macos => struct { | |
| 827 | fse: FsEvents, | |
| 828 | ||
| 829 | fn init() !Watch { | |
| 830 | return .{ | |
| 831 | .os = .{ .fse = try .init() }, | |
| 832 | .dir_count = 0, | |
| 833 | .dir_table = undefined, | |
| 834 | .generation = undefined, | |
| 835 | }; | |
| 836 | } | |
| 837 | fn update(w: *Watch, gpa: Allocator, steps: []const *Step) !void { | |
| 838 | try w.os.fse.setPaths(gpa, steps); | |
| 839 | w.dir_count = w.os.fse.watch_roots.len; | |
| 840 | } | |
| 841 | fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { | |
| 842 | return w.os.fse.wait(gpa, switch (timeout) { | |
| 843 | .none => null, | |
| 844 | .ms => |ms| @as(u64, ms) * std.time.ns_per_ms, | |
| 845 | }); | |
| 846 | } | |
| 847 | }, | |
| 815 | 848 | else => void, |
| 816 | 849 | }; |
| 817 | 850 |
lib/std/Build/Watch/FsEvents.zig created+493| ... | ... | @@ -0,0 +1,493 @@ |
| 1 | //! An implementation of file-system watching based on the `FSEventStream` API in macOS. | |
| 2 | //! While macOS supports kqueue, it does not allow detecting changes to files without | |
| 3 | //! placing watches on each individual file, meaning FD limits are reached incredibly | |
| 4 | //! quickly. The File System Events API works differently: it implements *recursive* | |
| 5 | //! directory watches, managed by a system service. Rather than being in libc, the API is | |
| 6 | //! exposed by the CoreServices framework. To avoid a compile dependency on the framework | |
| 7 | //! bundle, we dynamically load CoreServices with `std.DynLib`. | |
| 8 | //! | |
| 9 | //! While the logic in this file *is* specialized to `std.Build.Watch`, efforts have been | |
| 10 | //! made to keep that specialization to a minimum. Other use cases could be served with | |
| 11 | //! relatively minimal modifications to the `watch_paths` field and its usages (in | |
| 12 | //! particular the `setPaths` function). We avoid using the global GCD dispatch queue in | |
| 13 | //! favour of creating our own and synchronizing with an explicit semaphore, meaning this | |
| 14 | //! logic is thread-safe and does not affect process-global state. | |
| 15 | //! | |
| 16 | //! In theory, this API is quite good at avoiding filesystem race conditions. In practice, | |
| 17 | //! the logic that would avoid them is currently disabled, because the build system kind | |
| 18 | //! of relies on them at the time of writing to avoid redundant work -- see the comment at | |
| 19 | //! the top of `wait` for details. | |
| 20 | ||
| 21 | const enable_debug_logs = false; | |
| 22 | ||
| 23 | core_services: std.DynLib, | |
| 24 | resolved_symbols: ResolvedSymbols, | |
| 25 | ||
| 26 | paths_arena: std.heap.ArenaAllocator.State, | |
| 27 | /// The roots of the recursive watches. FSEvents has relatively small limits on the number | |
| 28 | /// of watched paths, so this slice must not be too long. The paths themselves are allocated | |
| 29 | /// into `paths_arena`, but this slice is allocated into the GPA. | |
| 30 | watch_roots: [][:0]const u8, | |
| 31 | /// All of the paths being watched. Value is the set of steps which depend on the file/directory. | |
| 32 | /// Keys and values are in `paths_arena`, but this map is allocated into the GPA. | |
| 33 | watch_paths: std.StringArrayHashMapUnmanaged([]const *std.Build.Step), | |
| 34 | ||
| 35 | /// The semaphore we use to block the thread calling `wait` until the callback determines a relevant | |
| 36 | /// event has occurred. This is retained across `wait` calls for simplicity and efficiency. | |
| 37 | waiting_semaphore: dispatch_semaphore_t, | |
| 38 | /// This dispatch queue is created by us and executes serially. It exists exclusively to trigger the | |
| 39 | /// callbacks of the FSEventStream we create. This is not in use outside of `wait`, but is retained | |
| 40 | /// across `wait` calls for simplicity and efficiency. | |
| 41 | dispatch_queue: dispatch_queue_t, | |
| 42 | /// In theory, this field avoids race conditions. In practice, it is essentially unused at the time | |
| 43 | /// of writing. See the comment at the start of `wait` for details. | |
| 44 | since_event: FSEventStreamEventId, | |
| 45 | ||
| 46 | /// All of the symbols we pull from the `dlopen`ed CoreServices framework. If any of these symbols | |
| 47 | /// is not present, `init` will close the framework and return an error. | |
| 48 | const ResolvedSymbols = struct { | |
| 49 | FSEventStreamCreate: *const fn ( | |
| 50 | allocator: CFAllocatorRef, | |
| 51 | callback: FSEventStreamCallback, | |
| 52 | ctx: ?*const FSEventStreamContext, | |
| 53 | paths_to_watch: CFArrayRef, | |
| 54 | since_when: FSEventStreamEventId, | |
| 55 | latency: CFTimeInterval, | |
| 56 | flags: FSEventStreamCreateFlags, | |
| 57 | ) callconv(.c) FSEventStreamRef, | |
| 58 | FSEventStreamSetDispatchQueue: *const fn (stream: FSEventStreamRef, queue: dispatch_queue_t) callconv(.c) void, | |
| 59 | FSEventStreamStart: *const fn (stream: FSEventStreamRef) callconv(.c) bool, | |
| 60 | FSEventStreamStop: *const fn (stream: FSEventStreamRef) callconv(.c) void, | |
| 61 | FSEventStreamInvalidate: *const fn (stream: FSEventStreamRef) callconv(.c) void, | |
| 62 | FSEventStreamRelease: *const fn (stream: FSEventStreamRef) callconv(.c) void, | |
| 63 | FSEventStreamGetLatestEventId: *const fn (stream: ConstFSEventStreamRef) callconv(.c) FSEventStreamEventId, | |
| 64 | FSEventsGetCurrentEventId: *const fn () callconv(.c) FSEventStreamEventId, | |
| 65 | CFRelease: *const fn (cf: *const anyopaque) callconv(.c) void, | |
| 66 | CFArrayCreate: *const fn ( | |
| 67 | allocator: CFAllocatorRef, | |
| 68 | values: [*]const usize, | |
| 69 | num_values: CFIndex, | |
| 70 | call_backs: ?*const CFArrayCallBacks, | |
| 71 | ) callconv(.c) CFArrayRef, | |
| 72 | CFStringCreateWithCString: *const fn ( | |
| 73 | alloc: CFAllocatorRef, | |
| 74 | c_str: [*:0]const u8, | |
| 75 | encoding: CFStringEncoding, | |
| 76 | ) callconv(.c) CFStringRef, | |
| 77 | CFAllocatorCreate: *const fn (allocator: CFAllocatorRef, context: *const CFAllocatorContext) callconv(.c) CFAllocatorRef, | |
| 78 | kCFAllocatorUseContext: *const CFAllocatorRef, | |
| 79 | }; | |
| 80 | ||
| 81 | pub fn init() error{ OpenFrameworkFailed, MissingCoreServicesSymbol }!FsEvents { | |
| 82 | var core_services = std.DynLib.open("/System/Library/Frameworks/CoreServices.framework/CoreServices") catch | |
| 83 | return error.OpenFrameworkFailed; | |
| 84 | errdefer core_services.close(); | |
| 85 | ||
| 86 | var resolved_symbols: ResolvedSymbols = undefined; | |
| 87 | inline for (@typeInfo(ResolvedSymbols).@"struct".fields) |f| { | |
| 88 | @field(resolved_symbols, f.name) = core_services.lookup(f.type, f.name) orelse return error.MissingCoreServicesSymbol; | |
| 89 | } | |
| 90 | ||
| 91 | return .{ | |
| 92 | .core_services = core_services, | |
| 93 | .resolved_symbols = resolved_symbols, | |
| 94 | .paths_arena = .{}, | |
| 95 | .watch_roots = &.{}, | |
| 96 | .watch_paths = .empty, | |
| 97 | .waiting_semaphore = dispatch_semaphore_create(0), | |
| 98 | .dispatch_queue = dispatch_queue_create("zig-watch", .SERIAL), | |
| 99 | // Not `.since_now`, because this means we can init `FsEvents` *before* we do work in order | |
| 100 | // to notice any changes which happened during said work. | |
| 101 | .since_event = resolved_symbols.FSEventsGetCurrentEventId(), | |
| 102 | }; | |
| 103 | } | |
| 104 | ||
| 105 | pub fn deinit(fse: *FsEvents, gpa: Allocator) void { | |
| 106 | dispatch_release(fse.waiting_semaphore); | |
| 107 | dispatch_release(fse.dispatch_queue); | |
| 108 | fse.core_services.close(); | |
| 109 | ||
| 110 | gpa.free(fse.watch_roots); | |
| 111 | fse.watch_paths.deinit(gpa); | |
| 112 | { | |
| 113 | var paths_arena = fse.paths_arena.promote(gpa); | |
| 114 | paths_arena.deinit(); | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) !void { | |
| 119 | var paths_arena_instance = fse.paths_arena.promote(gpa); | |
| 120 | defer fse.paths_arena = paths_arena_instance.state; | |
| 121 | const paths_arena = paths_arena_instance.allocator(); | |
| 122 | ||
| 123 | const cwd_path = try std.process.getCwdAlloc(gpa); | |
| 124 | defer gpa.free(cwd_path); | |
| 125 | ||
| 126 | var need_dirs: std.StringArrayHashMapUnmanaged(void) = .empty; | |
| 127 | defer need_dirs.deinit(gpa); | |
| 128 | ||
| 129 | fse.watch_paths.clearRetainingCapacity(); | |
| 130 | ||
| 131 | // We take `step` by pointer for a slight memory optimization in a moment. | |
| 132 | for (steps) |*step| { | |
| 133 | for (step.*.inputs.table.keys(), step.*.inputs.table.values()) |path, *files| { | |
| 134 | const resolved_dir = try std.fs.path.resolvePosix(paths_arena, &.{ cwd_path, path.root_dir.path orelse ".", path.sub_path }); | |
| 135 | try need_dirs.put(gpa, resolved_dir, {}); | |
| 136 | for (files.items) |file_name| { | |
| 137 | const watch_path = if (std.mem.eql(u8, file_name, ".")) | |
| 138 | resolved_dir | |
| 139 | else | |
| 140 | try std.fs.path.join(paths_arena, &.{ resolved_dir, file_name }); | |
| 141 | const gop = try fse.watch_paths.getOrPut(gpa, watch_path); | |
| 142 | if (gop.found_existing) { | |
| 143 | const old_steps = gop.value_ptr.*; | |
| 144 | const new_steps = try paths_arena.alloc(*std.Build.Step, old_steps.len + 1); | |
| 145 | @memcpy(new_steps[0..old_steps.len], old_steps); | |
| 146 | new_steps[old_steps.len] = step.*; | |
| 147 | gop.value_ptr.* = new_steps; | |
| 148 | } else { | |
| 149 | // This is why we captured `step` by pointer! We can avoid allocating a slice of one | |
| 150 | // step in the arena in the common case where a file is referenced by only one step. | |
| 151 | gop.value_ptr.* = step[0..1]; | |
| 152 | } | |
| 153 | } | |
| 154 | } | |
| 155 | } | |
| 156 | ||
| 157 | { | |
| 158 | // There's no point looking at directories inside other ones (e.g. "/foo" and "/foo/bar"). | |
| 159 | // To eliminate these, we'll re-add directories in order of path length with a redundancy check. | |
| 160 | const old_dirs = try gpa.dupe([]const u8, need_dirs.keys()); | |
| 161 | defer gpa.free(old_dirs); | |
| 162 | std.mem.sort([]const u8, old_dirs, {}, struct { | |
| 163 | fn lessThan(ctx: void, a: []const u8, b: []const u8) bool { | |
| 164 | ctx; | |
| 165 | return std.mem.lessThan(u8, a, b); | |
| 166 | } | |
| 167 | }.lessThan); | |
| 168 | need_dirs.clearRetainingCapacity(); | |
| 169 | for (old_dirs) |dir_path| { | |
| 170 | var it: std.fs.path.ComponentIterator(.posix, u8) = try .init(dir_path); | |
| 171 | while (it.next()) |component| { | |
| 172 | if (need_dirs.contains(component.path)) { | |
| 173 | // this path is '/foo/bar/qux', but '/foo' or '/foo/bar' was already added | |
| 174 | break; | |
| 175 | } | |
| 176 | } else { | |
| 177 | need_dirs.putAssumeCapacityNoClobber(dir_path, {}); | |
| 178 | } | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | // `need_dirs` is now a set of directories to watch with no redundancy. In practice, this is very | |
| 183 | // likely to have reduced it to a quite small set (e.g. it'll typically coalesce a full `src/` | |
| 184 | // directory into one entry). However, the FSEventStream API has a fairly low undocumented limit | |
| 185 | // on total watches (supposedly 4096), so we should handle the case where we exceed it. To be | |
| 186 | // safe, because this API can be a little unpredictable, we'll cap ourselves a little *below* | |
| 187 | // that known limit. | |
| 188 | if (need_dirs.count() > 2048) { | |
| 189 | // Fallback: watch the whole filesystem. This is excessive, but... it *works* :P | |
| 190 | if (enable_debug_logs) watch_log.debug("too many dirs; recursively watching root", .{}); | |
| 191 | fse.watch_roots = try gpa.realloc(fse.watch_roots, 1); | |
| 192 | fse.watch_roots[0] = "/"; | |
| 193 | } else { | |
| 194 | fse.watch_roots = try gpa.realloc(fse.watch_roots, need_dirs.count()); | |
| 195 | for (fse.watch_roots, need_dirs.keys()) |*out, in| { | |
| 196 | out.* = try paths_arena.dupeZ(u8, in); | |
| 197 | } | |
| 198 | } | |
| 199 | if (enable_debug_logs) { | |
| 200 | watch_log.debug("watching {d} paths using {d} recursive watches:", .{ fse.watch_paths.count(), fse.watch_roots.len }); | |
| 201 | for (fse.watch_roots) |dir_path| { | |
| 202 | watch_log.debug("- '{s}'", .{dir_path}); | |
| 203 | } | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | pub fn wait(fse: *FsEvents, gpa: Allocator, timeout_ns: ?u64) error{ OutOfMemory, StartFailed }!std.Build.Watch.WaitResult { | |
| 208 | if (fse.watch_roots.len == 0) @panic("nothing to watch"); | |
| 209 | ||
| 210 | const rs = fse.resolved_symbols; | |
| 211 | ||
| 212 | // At the time of writing, using `since_event` in the obvious way causes redundant rebuilds | |
| 213 | // to occur, because one step modifies a file which is an input to another step. The solution | |
| 214 | // to this problem will probably be either: | |
| 215 | // | |
| 216 | // a) Don't include the output of one step as a watch input of another; only mark external | |
| 217 | // files as watch inputs. Or... | |
| 218 | // | |
| 219 | // b) Note the current event ID when a step begins, and disregard events preceding that ID | |
| 220 | // when considering whether to dirty that step in `eventCallback`. | |
| 221 | // | |
| 222 | // For now, to avoid the redundant rebuilds, we bypass this `since_event` mechanism. This does | |
| 223 | // introduce race conditions, but the other `std.Build.Watch` implementations suffer from those | |
| 224 | // too at the time of writing, so this is kind of expected. | |
| 225 | fse.since_event = .since_now; | |
| 226 | ||
| 227 | const cf_allocator = rs.CFAllocatorCreate(rs.kCFAllocatorUseContext.*, &.{ | |
| 228 | .version = 0, | |
| 229 | .info = @constCast(&gpa), | |
| 230 | .retain = null, | |
| 231 | .release = null, | |
| 232 | .copy_description = null, | |
| 233 | .allocate = &cf_alloc_callbacks.allocate, | |
| 234 | .reallocate = &cf_alloc_callbacks.reallocate, | |
| 235 | .deallocate = &cf_alloc_callbacks.deallocate, | |
| 236 | .preferred_size = null, | |
| 237 | }) orelse return error.OutOfMemory; | |
| 238 | defer rs.CFRelease(cf_allocator); | |
| 239 | ||
| 240 | const cf_paths = try gpa.alloc(?CFStringRef, fse.watch_roots.len); | |
| 241 | @memset(cf_paths, null); | |
| 242 | defer { | |
| 243 | for (cf_paths) |o| if (o) |p| rs.CFRelease(p); | |
| 244 | gpa.free(cf_paths); | |
| 245 | } | |
| 246 | for (fse.watch_roots, cf_paths) |raw_path, *cf_path| { | |
| 247 | cf_path.* = rs.CFStringCreateWithCString(cf_allocator, raw_path, .utf8); | |
| 248 | } | |
| 249 | const cf_paths_array = rs.CFArrayCreate(cf_allocator, @ptrCast(cf_paths), @intCast(cf_paths.len), null); | |
| 250 | defer rs.CFRelease(cf_paths_array); | |
| 251 | ||
| 252 | const callback_ctx: EventCallbackCtx = .{ | |
| 253 | .fse = fse, | |
| 254 | .gpa = gpa, | |
| 255 | }; | |
| 256 | const event_stream = rs.FSEventStreamCreate( | |
| 257 | null, | |
| 258 | &eventCallback, | |
| 259 | &.{ | |
| 260 | .version = 0, | |
| 261 | .info = @constCast(&callback_ctx), | |
| 262 | .retain = null, | |
| 263 | .release = null, | |
| 264 | .copy_description = null, | |
| 265 | }, | |
| 266 | cf_paths_array, | |
| 267 | fse.since_event, | |
| 268 | 0.05, // 0.05s latency; higher values increase efficiency by coalescing more events | |
| 269 | .{ .watch_root = true, .file_events = true }, | |
| 270 | ); | |
| 271 | defer rs.FSEventStreamRelease(event_stream); | |
| 272 | rs.FSEventStreamSetDispatchQueue(event_stream, fse.dispatch_queue); | |
| 273 | defer rs.FSEventStreamInvalidate(event_stream); | |
| 274 | if (!rs.FSEventStreamStart(event_stream)) return error.StartFailed; | |
| 275 | defer rs.FSEventStreamStop(event_stream); | |
| 276 | const result = dispatch_semaphore_wait(fse.waiting_semaphore, timeout: { | |
| 277 | const ns = timeout_ns orelse break :timeout .forever; | |
| 278 | break :timeout dispatch_time(.now, @intCast(ns)); | |
| 279 | }); | |
| 280 | return switch (result) { | |
| 281 | 0 => .dirty, | |
| 282 | else => .timeout, | |
| 283 | }; | |
| 284 | } | |
| 285 | ||
| 286 | const cf_alloc_callbacks = struct { | |
| 287 | const log = std.log.scoped(.cf_alloc); | |
| 288 | fn allocate(size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) ?*const anyopaque { | |
| 289 | if (enable_debug_logs) log.debug("allocate {d}", .{size}); | |
| 290 | _ = hint; | |
| 291 | const gpa: *const Allocator = @ptrCast(@alignCast(info)); | |
| 292 | const mem = gpa.alignedAlloc(u8, .of(usize), @intCast(size + @sizeOf(usize))) catch return null; | |
| 293 | const metadata: *usize = @ptrCast(mem); | |
| 294 | metadata.* = @intCast(size); | |
| 295 | return mem[@sizeOf(usize)..].ptr; | |
| 296 | } | |
| 297 | fn reallocate(ptr: ?*anyopaque, new_size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) ?*const anyopaque { | |
| 298 | if (enable_debug_logs) log.debug("reallocate @{*} {d}", .{ ptr, new_size }); | |
| 299 | _ = hint; | |
| 300 | if (ptr == null or new_size == 0) return null; // not a bug: documentation explicitly states that realloc on NULL should return NULL | |
| 301 | const gpa: *const Allocator = @ptrCast(@alignCast(info)); | |
| 302 | const old_base: [*]align(@alignOf(usize)) u8 = @alignCast(@as([*]u8, @ptrCast(ptr)) - @sizeOf(usize)); | |
| 303 | const old_size = @as(*const usize, @ptrCast(old_base)).*; | |
| 304 | const old_mem = old_base[0 .. old_size + @sizeOf(usize)]; | |
| 305 | const new_mem = gpa.realloc(old_mem, @intCast(new_size + @sizeOf(usize))) catch return null; | |
| 306 | const metadata: *usize = @ptrCast(new_mem); | |
| 307 | metadata.* = @intCast(new_size); | |
| 308 | return new_mem[@sizeOf(usize)..].ptr; | |
| 309 | } | |
| 310 | fn deallocate(ptr: *anyopaque, info: ?*const anyopaque) callconv(.c) void { | |
| 311 | if (enable_debug_logs) log.debug("deallocate @{*}", .{ptr}); | |
| 312 | const gpa: *const Allocator = @ptrCast(@alignCast(info)); | |
| 313 | const old_base: [*]align(@alignOf(usize)) u8 = @alignCast(@as([*]u8, @ptrCast(ptr)) - @sizeOf(usize)); | |
| 314 | const old_size = @as(*const usize, @ptrCast(old_base)).*; | |
| 315 | const old_mem = old_base[0 .. old_size + @sizeOf(usize)]; | |
| 316 | gpa.free(old_mem); | |
| 317 | } | |
| 318 | }; | |
| 319 | ||
| 320 | const EventCallbackCtx = struct { | |
| 321 | fse: *FsEvents, | |
| 322 | gpa: Allocator, | |
| 323 | }; | |
| 324 | ||
| 325 | fn eventCallback( | |
| 326 | stream: ConstFSEventStreamRef, | |
| 327 | client_callback_info: ?*anyopaque, | |
| 328 | num_events: usize, | |
| 329 | events_paths_ptr: *anyopaque, | |
| 330 | events_flags_ptr: [*]const FSEventStreamEventFlags, | |
| 331 | events_ids_ptr: [*]const FSEventStreamEventId, | |
| 332 | ) callconv(.c) void { | |
| 333 | const ctx: *const EventCallbackCtx = @ptrCast(@alignCast(client_callback_info)); | |
| 334 | const fse = ctx.fse; | |
| 335 | const gpa = ctx.gpa; | |
| 336 | const rs = fse.resolved_symbols; | |
| 337 | const events_paths_ptr_casted: [*]const [*:0]const u8 = @ptrCast(@alignCast(events_paths_ptr)); | |
| 338 | const events_paths = events_paths_ptr_casted[0..num_events]; | |
| 339 | const events_ids = events_ids_ptr[0..num_events]; | |
| 340 | const events_flags = events_flags_ptr[0..num_events]; | |
| 341 | var any_dirty = false; | |
| 342 | for (events_paths, events_ids, events_flags) |event_path_nts, event_id, event_flags| { | |
| 343 | _ = event_id; | |
| 344 | if (event_flags.history_done) continue; // sentinel | |
| 345 | const event_path = std.mem.span(event_path_nts); | |
| 346 | switch (event_flags.must_scan_sub_dirs) { | |
| 347 | false => { | |
| 348 | if (fse.watch_paths.get(event_path)) |steps| { | |
| 349 | assert(steps.len > 0); | |
| 350 | for (steps) |s| dirtyStep(s, gpa, &any_dirty); | |
| 351 | } | |
| 352 | if (std.fs.path.dirname(event_path)) |event_dirname| { | |
| 353 | // Modifying '/foo/bar' triggers the watch on '/foo'. | |
| 354 | if (fse.watch_paths.get(event_dirname)) |steps| { | |
| 355 | assert(steps.len > 0); | |
| 356 | for (steps) |s| dirtyStep(s, gpa, &any_dirty); | |
| 357 | } | |
| 358 | } | |
| 359 | }, | |
| 360 | true => { | |
| 361 | // This is unlikely, but can occasionally happen when bottlenecked: events have been | |
| 362 | // coalesced into one. We want to see if any of these events are actually relevant | |
| 363 | // to us. The only way we can reasonably do that in this rare edge case is iterate | |
| 364 | // the watch paths and see if any is under this directory. That's acceptable because | |
| 365 | // we would otherwise kick off a rebuild which would be clearing those paths anyway. | |
| 366 | const changed_path = std.fs.path.dirname(event_path) orelse event_path; | |
| 367 | for (fse.watch_paths.keys(), fse.watch_paths.values()) |watching_path, steps| { | |
| 368 | if (dirStartsWith(watching_path, changed_path)) { | |
| 369 | for (steps) |s| dirtyStep(s, gpa, &any_dirty); | |
| 370 | } | |
| 371 | } | |
| 372 | }, | |
| 373 | } | |
| 374 | } | |
| 375 | if (any_dirty) { | |
| 376 | fse.since_event = rs.FSEventStreamGetLatestEventId(stream); | |
| 377 | _ = dispatch_semaphore_signal(fse.waiting_semaphore); | |
| 378 | } | |
| 379 | } | |
| 380 | fn dirtyStep(s: *std.Build.Step, gpa: Allocator, any_dirty: *bool) void { | |
| 381 | if (s.state == .precheck_done) return; | |
| 382 | s.recursiveReset(gpa); | |
| 383 | any_dirty.* = true; | |
| 384 | } | |
| 385 | fn dirStartsWith(path: []const u8, prefix: []const u8) bool { | |
| 386 | if (std.mem.eql(u8, path, prefix)) return true; | |
| 387 | if (!std.mem.startsWith(u8, path, prefix)) return false; | |
| 388 | if (path[prefix.len] != '/') return false; // `path` is `/foo/barx`, `prefix` is `/foo/bar` | |
| 389 | return true; // `path` is `/foo/bar/...`, `prefix` is `/foo/bar` | |
| 390 | } | |
| 391 | ||
| 392 | const dispatch_time_t = enum(u64) { | |
| 393 | now = 0, | |
| 394 | forever = std.math.maxInt(u64), | |
| 395 | _, | |
| 396 | }; | |
| 397 | extern fn dispatch_time(base: dispatch_time_t, delta_ns: i64) dispatch_time_t; | |
| 398 | ||
| 399 | const dispatch_semaphore_t = *opaque {}; | |
| 400 | extern fn dispatch_semaphore_create(value: isize) dispatch_semaphore_t; | |
| 401 | extern fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) isize; | |
| 402 | extern fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) isize; | |
| 403 | ||
| 404 | const dispatch_queue_t = *opaque {}; | |
| 405 | const dispatch_queue_attr_t = ?*opaque { | |
| 406 | const SERIAL: dispatch_queue_attr_t = null; | |
| 407 | }; | |
| 408 | extern fn dispatch_queue_create(label: [*:0]const u8, attr: dispatch_queue_attr_t) dispatch_queue_t; | |
| 409 | extern fn dispatch_release(object: *anyopaque) void; | |
| 410 | ||
| 411 | const CFAllocatorRef = ?*const opaque {}; | |
| 412 | const CFArrayRef = *const opaque {}; | |
| 413 | const CFStringRef = *const opaque {}; | |
| 414 | const CFTimeInterval = f64; | |
| 415 | const CFIndex = i32; | |
| 416 | const CFOptionFlags = enum(u32) { _ }; | |
| 417 | const CFAllocatorRetainCallBack = *const fn (info: ?*const anyopaque) callconv(.c) *const anyopaque; | |
| 418 | const CFAllocatorReleaseCallBack = *const fn (info: ?*const anyopaque) callconv(.c) void; | |
| 419 | const CFAllocatorCopyDescriptionCallBack = *const fn (info: ?*const anyopaque) callconv(.c) CFStringRef; | |
| 420 | const CFAllocatorAllocateCallBack = *const fn (alloc_size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) ?*const anyopaque; | |
| 421 | const CFAllocatorReallocateCallBack = *const fn (ptr: ?*anyopaque, new_size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) ?*const anyopaque; | |
| 422 | const CFAllocatorDeallocateCallBack = *const fn (ptr: *anyopaque, info: ?*const anyopaque) callconv(.c) void; | |
| 423 | const CFAllocatorPreferredSizeCallBack = *const fn (size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) CFIndex; | |
| 424 | const CFAllocatorContext = extern struct { | |
| 425 | version: CFIndex, | |
| 426 | info: ?*anyopaque, | |
| 427 | retain: ?CFAllocatorRetainCallBack, | |
| 428 | release: ?CFAllocatorReleaseCallBack, | |
| 429 | copy_description: ?CFAllocatorCopyDescriptionCallBack, | |
| 430 | allocate: CFAllocatorAllocateCallBack, | |
| 431 | reallocate: ?CFAllocatorReallocateCallBack, | |
| 432 | deallocate: ?CFAllocatorDeallocateCallBack, | |
| 433 | preferred_size: ?CFAllocatorPreferredSizeCallBack, | |
| 434 | }; | |
| 435 | const CFArrayCallBacks = opaque {}; | |
| 436 | const CFStringEncoding = enum(u32) { | |
| 437 | invalid_id = std.math.maxInt(u32), | |
| 438 | mac_roman = 0, | |
| 439 | windows_latin_1 = 0x500, | |
| 440 | iso_latin_1 = 0x201, | |
| 441 | next_step_latin = 0xB01, | |
| 442 | ascii = 0x600, | |
| 443 | unicode = 0x100, | |
| 444 | utf8 = 0x8000100, | |
| 445 | non_lossy_ascii = 0xBFF, | |
| 446 | }; | |
| 447 | ||
| 448 | const FSEventStreamRef = *opaque {}; | |
| 449 | const ConstFSEventStreamRef = *const @typeInfo(FSEventStreamRef).pointer.child; | |
| 450 | const FSEventStreamCallback = *const fn ( | |
| 451 | stream: ConstFSEventStreamRef, | |
| 452 | client_callback_info: ?*anyopaque, | |
| 453 | num_events: usize, | |
| 454 | event_paths: *anyopaque, | |
| 455 | event_flags: [*]const FSEventStreamEventFlags, | |
| 456 | event_ids: [*]const FSEventStreamEventId, | |
| 457 | ) callconv(.c) void; | |
| 458 | const FSEventStreamContext = extern struct { | |
| 459 | version: CFIndex, | |
| 460 | info: ?*anyopaque, | |
| 461 | retain: ?CFAllocatorRetainCallBack, | |
| 462 | release: ?CFAllocatorReleaseCallBack, | |
| 463 | copy_description: ?CFAllocatorCopyDescriptionCallBack, | |
| 464 | }; | |
| 465 | const FSEventStreamEventId = enum(u64) { | |
| 466 | since_now = std.math.maxInt(u64), | |
| 467 | _, | |
| 468 | }; | |
| 469 | const FSEventStreamCreateFlags = packed struct(u32) { | |
| 470 | use_cf_types: bool = false, | |
| 471 | no_defer: bool = false, | |
| 472 | watch_root: bool = false, | |
| 473 | ignore_self: bool = false, | |
| 474 | file_events: bool = false, | |
| 475 | _: u27 = 0, | |
| 476 | }; | |
| 477 | const FSEventStreamEventFlags = packed struct(u32) { | |
| 478 | must_scan_sub_dirs: bool, | |
| 479 | user_dropped: bool, | |
| 480 | kernel_dropped: bool, | |
| 481 | event_ids_wrapped: bool, | |
| 482 | history_done: bool, | |
| 483 | root_changed: bool, | |
| 484 | mount: bool, | |
| 485 | unmount: bool, | |
| 486 | _: u24 = 0, | |
| 487 | }; | |
| 488 | ||
| 489 | const std = @import("std"); | |
| 490 | const assert = std.debug.assert; | |
| 491 | const Allocator = std.mem.Allocator; | |
| 492 | const watch_log = std.log.scoped(.watch); | |
| 493 | const FsEvents = @This(); |
lib/std/Build/WebServer.zig+42-53| ... | ... | @@ -251,48 +251,44 @@ pub fn now(s: *const WebServer) i64 { |
| 251 | 251 | fn accept(ws: *WebServer, connection: std.net.Server.Connection) void { |
| 252 | 252 | defer connection.stream.close(); |
| 253 | 253 | |
| 254 | var read_buf: [0x4000]u8 = undefined; | |
| 255 | var server: std.http.Server = .init(connection, &read_buf); | |
| 254 | var send_buffer: [4096]u8 = undefined; | |
| 255 | var recv_buffer: [4096]u8 = undefined; | |
| 256 | var connection_reader = connection.stream.reader(&recv_buffer); | |
| 257 | var connection_writer = connection.stream.writer(&send_buffer); | |
| 258 | var server: http.Server = .init(connection_reader.interface(), &connection_writer.interface); | |
| 256 | 259 | |
| 257 | 260 | while (true) { |
| 258 | 261 | var request = server.receiveHead() catch |err| switch (err) { |
| 259 | 262 | error.HttpConnectionClosing => return, |
| 260 | else => { | |
| 261 | log.err("failed to receive http request: {s}", .{@errorName(err)}); | |
| 262 | return; | |
| 263 | }, | |
| 263 | else => return log.err("failed to receive http request: {t}", .{err}), | |
| 264 | 264 | }; |
| 265 | var ws_send_buf: [0x4000]u8 = undefined; | |
| 266 | var ws_recv_buf: [0x4000]u8 align(4) = undefined; | |
| 267 | if (std.http.WebSocket.init(&request, &ws_send_buf, &ws_recv_buf) catch |err| { | |
| 268 | log.err("failed to initialize websocket connection: {s}", .{@errorName(err)}); | |
| 269 | return; | |
| 270 | }) |ws_init| { | |
| 271 | var web_socket = ws_init; | |
| 272 | ws.serveWebSocket(&web_socket) catch |err| { | |
| 273 | log.err("failed to serve websocket: {s}", .{@errorName(err)}); | |
| 274 | return; | |
| 275 | }; | |
| 276 | comptime unreachable; | |
| 277 | } else { | |
| 278 | ws.serveRequest(&request) catch |err| switch (err) { | |
| 279 | error.AlreadyReported => return, | |
| 280 | else => { | |
| 281 | log.err("failed to serve '{s}': {s}", .{ request.head.target, @errorName(err) }); | |
| 265 | switch (request.upgradeRequested()) { | |
| 266 | .websocket => |opt_key| { | |
| 267 | const key = opt_key orelse return log.err("missing websocket key", .{}); | |
| 268 | var web_socket = request.respondWebSocket(.{ .key = key }) catch { | |
| 269 | return log.err("failed to respond web socket: {t}", .{connection_writer.err.?}); | |
| 270 | }; | |
| 271 | ws.serveWebSocket(&web_socket) catch |err| { | |
| 272 | log.err("failed to serve websocket: {t}", .{err}); | |
| 282 | 273 | return; |
| 283 | }, | |
| 284 | }; | |
| 274 | }; | |
| 275 | comptime unreachable; | |
| 276 | }, | |
| 277 | .other => |name| return log.err("unknown upgrade request: {s}", .{name}), | |
| 278 | .none => { | |
| 279 | ws.serveRequest(&request) catch |err| switch (err) { | |
| 280 | error.AlreadyReported => return, | |
| 281 | else => { | |
| 282 | log.err("failed to serve '{s}': {t}", .{ request.head.target, err }); | |
| 283 | return; | |
| 284 | }, | |
| 285 | }; | |
| 286 | }, | |
| 285 | 287 | } |
| 286 | 288 | } |
| 287 | 289 | } |
| 288 | 290 | |
| 289 | fn makeIov(s: []const u8) std.posix.iovec_const { | |
| 290 | return .{ | |
| 291 | .base = s.ptr, | |
| 292 | .len = s.len, | |
| 293 | }; | |
| 294 | } | |
| 295 | fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn { | |
| 291 | fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn { | |
| 296 | 292 | var prev_build_status = ws.build_status.load(.monotonic); |
| 297 | 293 | |
| 298 | 294 | const prev_step_status_bits = try ws.gpa.alloc(u8, ws.step_status_bits.len); |
| ... | ... | @@ -312,11 +308,8 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn { |
| 312 | 308 | .timestamp = ws.now(), |
| 313 | 309 | .steps_len = @intCast(ws.all_steps.len), |
| 314 | 310 | }; |
| 315 | try sock.writeMessagev(&.{ | |
| 316 | makeIov(@ptrCast(&hello_header)), | |
| 317 | makeIov(ws.step_names_trailing), | |
| 318 | makeIov(prev_step_status_bits), | |
| 319 | }, .binary); | |
| 311 | var bufs: [3][]const u8 = .{ @ptrCast(&hello_header), ws.step_names_trailing, prev_step_status_bits }; | |
| 312 | try sock.writeMessageVec(&bufs, .binary); | |
| 320 | 313 | } |
| 321 | 314 | |
| 322 | 315 | var prev_fuzz: Fuzz.Previous = .init; |
| ... | ... | @@ -380,7 +373,7 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn { |
| 380 | 373 | std.Thread.Futex.timedWait(&ws.update_id, start_update_id, std.time.ns_per_ms * default_update_interval_ms) catch {}; |
| 381 | 374 | } |
| 382 | 375 | } |
| 383 | fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void { | |
| 376 | fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void { | |
| 384 | 377 | while (true) { |
| 385 | 378 | const msg = sock.readSmallMessage() catch return; |
| 386 | 379 | if (msg.opcode != .binary) continue; |
| ... | ... | @@ -402,7 +395,7 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void { |
| 402 | 395 | } |
| 403 | 396 | } |
| 404 | 397 | |
| 405 | fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void { | |
| 398 | fn serveRequest(ws: *WebServer, req: *http.Server.Request) !void { | |
| 406 | 399 | // Strip an optional leading '/debug' component from the request. |
| 407 | 400 | const target: []const u8, const debug: bool = target: { |
| 408 | 401 | if (mem.eql(u8, req.head.target, "/debug")) break :target .{ "/", true }; |
| ... | ... | @@ -431,7 +424,7 @@ fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void { |
| 431 | 424 | |
| 432 | 425 | fn serveLibFile( |
| 433 | 426 | ws: *WebServer, |
| 434 | request: *std.http.Server.Request, | |
| 427 | request: *http.Server.Request, | |
| 435 | 428 | sub_path: []const u8, |
| 436 | 429 | content_type: []const u8, |
| 437 | 430 | ) !void { |
| ... | ... | @@ -442,7 +435,7 @@ fn serveLibFile( |
| 442 | 435 | } |
| 443 | 436 | fn serveClientWasm( |
| 444 | 437 | ws: *WebServer, |
| 445 | req: *std.http.Server.Request, | |
| 438 | req: *http.Server.Request, | |
| 446 | 439 | optimize_mode: std.builtin.OptimizeMode, |
| 447 | 440 | ) !void { |
| 448 | 441 | var arena_state: std.heap.ArenaAllocator = .init(ws.gpa); |
| ... | ... | @@ -456,12 +449,12 @@ fn serveClientWasm( |
| 456 | 449 | |
| 457 | 450 | pub fn serveFile( |
| 458 | 451 | ws: *WebServer, |
| 459 | request: *std.http.Server.Request, | |
| 452 | request: *http.Server.Request, | |
| 460 | 453 | path: Cache.Path, |
| 461 | 454 | content_type: []const u8, |
| 462 | 455 | ) !void { |
| 463 | 456 | const gpa = ws.gpa; |
| 464 | // The desired API is actually sendfile, which will require enhancing std.http.Server. | |
| 457 | // The desired API is actually sendfile, which will require enhancing http.Server. | |
| 465 | 458 | // We load the file with every request so that the user can make changes to the file |
| 466 | 459 | // and refresh the HTML page without restarting this server. |
| 467 | 460 | const file_contents = path.root_dir.handle.readFileAlloc(gpa, path.sub_path, 10 * 1024 * 1024) catch |err| { |
| ... | ... | @@ -478,14 +471,13 @@ pub fn serveFile( |
| 478 | 471 | } |
| 479 | 472 | pub fn serveTarFile( |
| 480 | 473 | ws: *WebServer, |
| 481 | request: *std.http.Server.Request, | |
| 474 | request: *http.Server.Request, | |
| 482 | 475 | paths: []const Cache.Path, |
| 483 | 476 | ) !void { |
| 484 | 477 | const gpa = ws.gpa; |
| 485 | 478 | |
| 486 | var send_buf: [0x4000]u8 = undefined; | |
| 487 | var response = request.respondStreaming(.{ | |
| 488 | .send_buffer = &send_buf, | |
| 479 | var send_buffer: [0x4000]u8 = undefined; | |
| 480 | var response = try request.respondStreaming(&send_buffer, .{ | |
| 489 | 481 | .respond_options = .{ |
| 490 | 482 | .extra_headers = &.{ |
| 491 | 483 | .{ .name = "Content-Type", .value = "application/x-tar" }, |
| ... | ... | @@ -497,10 +489,7 @@ pub fn serveTarFile( |
| 497 | 489 | var cached_cwd_path: ?[]const u8 = null; |
| 498 | 490 | defer if (cached_cwd_path) |p| gpa.free(p); |
| 499 | 491 | |
| 500 | var response_buf: [1024]u8 = undefined; | |
| 501 | var adapter = response.writer().adaptToNewApi(); | |
| 502 | adapter.new_interface.buffer = &response_buf; | |
| 503 | var archiver: std.tar.Writer = .{ .underlying_writer = &adapter.new_interface }; | |
| 492 | var archiver: std.tar.Writer = .{ .underlying_writer = &response.writer }; | |
| 504 | 493 | |
| 505 | 494 | for (paths) |path| { |
| 506 | 495 | var file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| { |
| ... | ... | @@ -526,7 +515,6 @@ pub fn serveTarFile( |
| 526 | 515 | } |
| 527 | 516 | |
| 528 | 517 | // intentionally not calling `archiver.finishPedantically` |
| 529 | try adapter.new_interface.flush(); | |
| 530 | 518 | try response.end(); |
| 531 | 519 | } |
| 532 | 520 | |
| ... | ... | @@ -804,7 +792,7 @@ pub fn wait(ws: *WebServer) RunnerRequest { |
| 804 | 792 | } |
| 805 | 793 | } |
| 806 | 794 | |
| 807 | const cache_control_header: std.http.Header = .{ | |
| 795 | const cache_control_header: http.Header = .{ | |
| 808 | 796 | .name = "Cache-Control", |
| 809 | 797 | .value = "max-age=0, must-revalidate", |
| 810 | 798 | }; |
| ... | ... | @@ -819,5 +807,6 @@ const Build = std.Build; |
| 819 | 807 | const Cache = Build.Cache; |
| 820 | 808 | const Fuzz = Build.Fuzz; |
| 821 | 809 | const abi = Build.abi; |
| 810 | const http = std.http; | |
| 822 | 811 | |
| 823 | 812 | const WebServer = @This(); |
lib/std/Io/DeprecatedReader.zig-27| ... | ... | @@ -249,33 +249,6 @@ pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes |
| 249 | 249 | return bytes; |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | /// Reads bytes until `bounded.len` is equal to `num_bytes`, | |
| 253 | /// or the stream ends. | |
| 254 | /// | |
| 255 | /// * it is assumed that `num_bytes` will not exceed `bounded.capacity()` | |
| 256 | pub fn readIntoBoundedBytes( | |
| 257 | self: Self, | |
| 258 | comptime num_bytes: usize, | |
| 259 | bounded: *std.BoundedArray(u8, num_bytes), | |
| 260 | ) anyerror!void { | |
| 261 | while (bounded.len < num_bytes) { | |
| 262 | // get at most the number of bytes free in the bounded array | |
| 263 | const bytes_read = try self.read(bounded.unusedCapacitySlice()); | |
| 264 | if (bytes_read == 0) return; | |
| 265 | ||
| 266 | // bytes_read will never be larger than @TypeOf(bounded.len) | |
| 267 | // due to `self.read` being bounded by `bounded.unusedCapacitySlice()` | |
| 268 | bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read)); | |
| 269 | } | |
| 270 | } | |
| 271 | ||
| 272 | /// Reads at most `num_bytes` and returns as a bounded array. | |
| 273 | pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) { | |
| 274 | var result = std.BoundedArray(u8, num_bytes){}; | |
| 275 | try self.readIntoBoundedBytes(num_bytes, &result); | |
| 276 | return result; | |
| 277 | } | |
| 278 | ||
| 279 | 252 | pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T { |
| 280 | 253 | const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8)); |
| 281 | 254 | return mem.readInt(T, &bytes, endian); |
lib/std/Io/Reader.zig+105-87| ... | ... | @@ -70,13 +70,14 @@ pub const VTable = struct { |
| 70 | 70 | |
| 71 | 71 | /// Returns number of bytes written to `data`. |
| 72 | 72 | /// |
| 73 | /// `data` may not have nonzero length. | |
| 73 | /// `data` must have nonzero length. `data[0]` may have zero length, in | |
| 74 | /// which case the implementation must write to `Reader.buffer`. | |
| 74 | 75 | /// |
| 75 | 76 | /// `data` may not contain an alias to `Reader.buffer`. |
| 76 | 77 | /// |
| 77 | /// `data` is mutable because the implementation may to temporarily modify | |
| 78 | /// the fields in order to handle partial reads. Implementations must | |
| 79 | /// restore the original value before returning. | |
| 78 | /// `data` is mutable because the implementation may temporarily modify the | |
| 79 | /// fields in order to handle partial reads. Implementations must restore | |
| 80 | /// the original value before returning. | |
| 80 | 81 | /// |
| 81 | 82 | /// Implementations may ignore `data`, writing directly to `Reader.buffer`, |
| 82 | 83 | /// modifying `seek` and `end` accordingly, and returning 0 from this |
| ... | ... | @@ -366,8 +367,11 @@ pub fn appendRemainingUnlimited( |
| 366 | 367 | const buffer_contents = r.buffer[r.seek..r.end]; |
| 367 | 368 | try list.ensureUnusedCapacity(gpa, buffer_contents.len + bump); |
| 368 | 369 | list.appendSliceAssumeCapacity(buffer_contents); |
| 369 | r.seek = 0; | |
| 370 | r.end = 0; | |
| 370 | // If statement protects `ending`. | |
| 371 | if (r.end != 0) { | |
| 372 | r.seek = 0; | |
| 373 | r.end = 0; | |
| 374 | } | |
| 371 | 375 | // From here, we leave `buffer` empty, appending directly to `list`. |
| 372 | 376 | var writer: Writer = .{ |
| 373 | 377 | .buffer = undefined, |
| ... | ... | @@ -421,23 +425,29 @@ pub fn readVec(r: *Reader, data: [][]u8) Error!usize { |
| 421 | 425 | |
| 422 | 426 | /// Writes to `Reader.buffer` or `data`, whichever has larger capacity. |
| 423 | 427 | pub fn defaultReadVec(r: *Reader, data: [][]u8) Error!usize { |
| 424 | assert(r.seek == r.end); | |
| 425 | r.seek = 0; | |
| 426 | r.end = 0; | |
| 427 | 428 | const first = data[0]; |
| 428 | const direct = first.len >= r.buffer.len; | |
| 429 | if (r.seek == r.end and first.len >= r.buffer.len) { | |
| 430 | var writer: Writer = .{ | |
| 431 | .buffer = first, | |
| 432 | .end = 0, | |
| 433 | .vtable = &.{ .drain = Writer.fixedDrain }, | |
| 434 | }; | |
| 435 | const limit: Limit = .limited(writer.buffer.len - writer.end); | |
| 436 | return r.vtable.stream(r, &writer, limit) catch |err| switch (err) { | |
| 437 | error.WriteFailed => unreachable, | |
| 438 | else => |e| return e, | |
| 439 | }; | |
| 440 | } | |
| 429 | 441 | var writer: Writer = .{ |
| 430 | .buffer = if (direct) first else r.buffer, | |
| 431 | .end = 0, | |
| 442 | .buffer = r.buffer, | |
| 443 | .end = r.end, | |
| 432 | 444 | .vtable = &.{ .drain = Writer.fixedDrain }, |
| 433 | 445 | }; |
| 434 | 446 | const limit: Limit = .limited(writer.buffer.len - writer.end); |
| 435 | const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) { | |
| 447 | r.end += r.vtable.stream(r, &writer, limit) catch |err| switch (err) { | |
| 436 | 448 | error.WriteFailed => unreachable, |
| 437 | 449 | else => |e| return e, |
| 438 | 450 | }; |
| 439 | if (direct) return n; | |
| 440 | r.end += n; | |
| 441 | 451 | return 0; |
| 442 | 452 | } |
| 443 | 453 | |
| ... | ... | @@ -1059,17 +1069,8 @@ pub fn fill(r: *Reader, n: usize) Error!void { |
| 1059 | 1069 | /// increasing by a factor of 5 or more. |
| 1060 | 1070 | fn fillUnbuffered(r: *Reader, n: usize) Error!void { |
| 1061 | 1071 | try rebase(r, n); |
| 1062 | var writer: Writer = .{ | |
| 1063 | .buffer = r.buffer, | |
| 1064 | .vtable = &.{ .drain = Writer.fixedDrain }, | |
| 1065 | }; | |
| 1066 | while (r.end < r.seek + n) { | |
| 1067 | writer.end = r.end; | |
| 1068 | r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { | |
| 1069 | error.WriteFailed => unreachable, | |
| 1070 | error.ReadFailed, error.EndOfStream => |e| return e, | |
| 1071 | }; | |
| 1072 | } | |
| 1072 | var bufs: [1][]u8 = .{""}; | |
| 1073 | while (r.end < r.seek + n) _ = try r.vtable.readVec(r, &bufs); | |
| 1073 | 1074 | } |
| 1074 | 1075 | |
| 1075 | 1076 | /// Without advancing the seek position, does exactly one underlying read, filling the buffer as |
| ... | ... | @@ -1079,15 +1080,8 @@ fn fillUnbuffered(r: *Reader, n: usize) Error!void { |
| 1079 | 1080 | /// Asserts buffer capacity is at least 1. |
| 1080 | 1081 | pub fn fillMore(r: *Reader) Error!void { |
| 1081 | 1082 | try rebase(r, 1); |
| 1082 | var writer: Writer = .{ | |
| 1083 | .buffer = r.buffer, | |
| 1084 | .end = r.end, | |
| 1085 | .vtable = &.{ .drain = Writer.fixedDrain }, | |
| 1086 | }; | |
| 1087 | r.end += r.vtable.stream(r, &writer, .limited(r.buffer.len - r.end)) catch |err| switch (err) { | |
| 1088 | error.WriteFailed => unreachable, | |
| 1089 | else => |e| return e, | |
| 1090 | }; | |
| 1083 | var bufs: [1][]u8 = .{""}; | |
| 1084 | _ = try r.vtable.readVec(r, &bufs); | |
| 1091 | 1085 | } |
| 1092 | 1086 | |
| 1093 | 1087 | /// Returns the next byte from the stream or returns `error.EndOfStream`. |
| ... | ... | @@ -1315,31 +1309,6 @@ pub fn defaultRebase(r: *Reader, capacity: usize) RebaseError!void { |
| 1315 | 1309 | r.end = data.len; |
| 1316 | 1310 | } |
| 1317 | 1311 | |
| 1318 | /// Advances the stream and decreases the size of the storage buffer by `n`, | |
| 1319 | /// returning the range of bytes no longer accessible by `r`. | |
| 1320 | /// | |
| 1321 | /// This action can be undone by `restitute`. | |
| 1322 | /// | |
| 1323 | /// Asserts there are at least `n` buffered bytes already. | |
| 1324 | /// | |
| 1325 | /// Asserts that `r.seek` is zero, i.e. the buffer is in a rebased state. | |
| 1326 | pub fn steal(r: *Reader, n: usize) []u8 { | |
| 1327 | assert(r.seek == 0); | |
| 1328 | assert(n <= r.end); | |
| 1329 | const stolen = r.buffer[0..n]; | |
| 1330 | r.buffer = r.buffer[n..]; | |
| 1331 | r.end -= n; | |
| 1332 | return stolen; | |
| 1333 | } | |
| 1334 | ||
| 1335 | /// Expands the storage buffer, undoing the effects of `steal` | |
| 1336 | /// Assumes that `n` does not exceed the total number of stolen bytes. | |
| 1337 | pub fn restitute(r: *Reader, n: usize) void { | |
| 1338 | r.buffer = (r.buffer.ptr - n)[0 .. r.buffer.len + n]; | |
| 1339 | r.end += n; | |
| 1340 | r.seek += n; | |
| 1341 | } | |
| 1342 | ||
| 1343 | 1312 | test fixed { |
| 1344 | 1313 | var r: Reader = .fixed("a\x02"); |
| 1345 | 1314 | try testing.expect((try r.takeByte()) == 'a'); |
| ... | ... | @@ -1796,18 +1765,26 @@ pub fn Hashed(comptime Hasher: type) type { |
| 1796 | 1765 | |
| 1797 | 1766 | fn readVec(r: *Reader, data: [][]u8) Error!usize { |
| 1798 | 1767 | const this: *@This() = @alignCast(@fieldParentPtr("reader", r)); |
| 1799 | const n = try this.in.readVec(data); | |
| 1768 | var vecs: [8][]u8 = undefined; // Arbitrarily chosen amount. | |
| 1769 | const dest_n, const data_size = try r.writableVector(&vecs, data); | |
| 1770 | const dest = vecs[0..dest_n]; | |
| 1771 | const n = try this.in.readVec(dest); | |
| 1800 | 1772 | var remaining: usize = n; |
| 1801 | for (data) |slice| { | |
| 1773 | for (dest) |slice| { | |
| 1802 | 1774 | if (remaining < slice.len) { |
| 1803 | 1775 | this.hasher.update(slice[0..remaining]); |
| 1804 | return n; | |
| 1776 | remaining = 0; | |
| 1777 | break; | |
| 1805 | 1778 | } else { |
| 1806 | 1779 | remaining -= slice.len; |
| 1807 | 1780 | this.hasher.update(slice); |
| 1808 | 1781 | } |
| 1809 | 1782 | } |
| 1810 | 1783 | assert(remaining == 0); |
| 1784 | if (n > data_size) { | |
| 1785 | r.end += n - data_size; | |
| 1786 | return data_size; | |
| 1787 | } | |
| 1811 | 1788 | return n; |
| 1812 | 1789 | } |
| 1813 | 1790 | |
| ... | ... | @@ -1824,17 +1801,24 @@ pub fn Hashed(comptime Hasher: type) type { |
| 1824 | 1801 | pub fn writableVectorPosix(r: *Reader, buffer: []std.posix.iovec, data: []const []u8) Error!struct { usize, usize } { |
| 1825 | 1802 | var i: usize = 0; |
| 1826 | 1803 | var n: usize = 0; |
| 1827 | for (data) |buf| { | |
| 1828 | if (buffer.len - i == 0) return .{ i, n }; | |
| 1804 | if (r.seek == r.end) { | |
| 1805 | for (data) |buf| { | |
| 1806 | if (buffer.len - i == 0) return .{ i, n }; | |
| 1807 | if (buf.len != 0) { | |
| 1808 | buffer[i] = .{ .base = buf.ptr, .len = buf.len }; | |
| 1809 | i += 1; | |
| 1810 | n += buf.len; | |
| 1811 | } | |
| 1812 | } | |
| 1813 | const buf = r.buffer; | |
| 1829 | 1814 | if (buf.len != 0) { |
| 1815 | r.seek = 0; | |
| 1816 | r.end = 0; | |
| 1830 | 1817 | buffer[i] = .{ .base = buf.ptr, .len = buf.len }; |
| 1831 | 1818 | i += 1; |
| 1832 | n += buf.len; | |
| 1833 | 1819 | } |
| 1834 | } | |
| 1835 | assert(r.seek == r.end); | |
| 1836 | const buf = r.buffer; | |
| 1837 | if (buf.len != 0) { | |
| 1820 | } else { | |
| 1821 | const buf = r.buffer[r.end..]; | |
| 1838 | 1822 | buffer[i] = .{ .base = buf.ptr, .len = buf.len }; |
| 1839 | 1823 | i += 1; |
| 1840 | 1824 | } |
| ... | ... | @@ -1848,28 +1832,62 @@ pub fn writableVectorWsa( |
| 1848 | 1832 | ) Error!struct { usize, usize } { |
| 1849 | 1833 | var i: usize = 0; |
| 1850 | 1834 | var n: usize = 0; |
| 1851 | for (data) |buf| { | |
| 1852 | if (buffer.len - i == 0) return .{ i, n }; | |
| 1853 | if (buf.len == 0) continue; | |
| 1854 | if (std.math.cast(u32, buf.len)) |len| { | |
| 1855 | buffer[i] = .{ .buf = buf.ptr, .len = len }; | |
| 1835 | if (r.seek == r.end) { | |
| 1836 | for (data) |buf| { | |
| 1837 | if (buffer.len - i == 0) return .{ i, n }; | |
| 1838 | if (buf.len == 0) continue; | |
| 1839 | if (std.math.cast(u32, buf.len)) |len| { | |
| 1840 | buffer[i] = .{ .buf = buf.ptr, .len = len }; | |
| 1841 | i += 1; | |
| 1842 | n += len; | |
| 1843 | continue; | |
| 1844 | } | |
| 1845 | buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) }; | |
| 1856 | 1846 | i += 1; |
| 1857 | n += len; | |
| 1858 | continue; | |
| 1847 | n += std.math.maxInt(u32); | |
| 1848 | return .{ i, n }; | |
| 1859 | 1849 | } |
| 1860 | buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) }; | |
| 1850 | const buf = r.buffer; | |
| 1851 | if (buf.len != 0) { | |
| 1852 | r.seek = 0; | |
| 1853 | r.end = 0; | |
| 1854 | if (std.math.cast(u32, buf.len)) |len| { | |
| 1855 | buffer[i] = .{ .buf = buf.ptr, .len = len }; | |
| 1856 | } else { | |
| 1857 | buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) }; | |
| 1858 | } | |
| 1859 | i += 1; | |
| 1860 | } | |
| 1861 | } else { | |
| 1862 | buffer[i] = .{ | |
| 1863 | .buf = r.buffer.ptr + r.end, | |
| 1864 | .len = @min(std.math.maxInt(u32), r.buffer.len - r.end), | |
| 1865 | }; | |
| 1861 | 1866 | i += 1; |
| 1862 | n += std.math.maxInt(u32); | |
| 1863 | return .{ i, n }; | |
| 1864 | 1867 | } |
| 1865 | assert(r.seek == r.end); | |
| 1866 | const buf = r.buffer; | |
| 1867 | if (buf.len != 0) { | |
| 1868 | if (std.math.cast(u32, buf.len)) |len| { | |
| 1869 | buffer[i] = .{ .buf = buf.ptr, .len = len }; | |
| 1870 | } else { | |
| 1871 | buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) }; | |
| 1868 | return .{ i, n }; | |
| 1869 | } | |
| 1870 | ||
| 1871 | pub fn writableVector(r: *Reader, buffer: [][]u8, data: []const []u8) Error!struct { usize, usize } { | |
| 1872 | var i: usize = 0; | |
| 1873 | var n: usize = 0; | |
| 1874 | if (r.seek == r.end) { | |
| 1875 | for (data) |buf| { | |
| 1876 | if (buffer.len - i == 0) return .{ i, n }; | |
| 1877 | if (buf.len != 0) { | |
| 1878 | buffer[i] = buf; | |
| 1879 | i += 1; | |
| 1880 | n += buf.len; | |
| 1881 | } | |
| 1882 | } | |
| 1883 | if (r.buffer.len != 0) { | |
| 1884 | r.seek = 0; | |
| 1885 | r.end = 0; | |
| 1886 | buffer[i] = r.buffer; | |
| 1887 | i += 1; | |
| 1872 | 1888 | } |
| 1889 | } else { | |
| 1890 | buffer[i] = r.buffer[r.end..]; | |
| 1873 | 1891 | i += 1; |
| 1874 | 1892 | } |
| 1875 | 1893 | return .{ i, n }; |
lib/std/Io/Writer.zig+77-19| ... | ... | @@ -191,29 +191,87 @@ pub fn writeSplatHeader( |
| 191 | 191 | data: []const []const u8, |
| 192 | 192 | splat: usize, |
| 193 | 193 | ) Error!usize { |
| 194 | const new_end = w.end + header.len; | |
| 195 | if (new_end <= w.buffer.len) { | |
| 196 | @memcpy(w.buffer[w.end..][0..header.len], header); | |
| 197 | w.end = new_end; | |
| 198 | return header.len + try writeSplat(w, data, splat); | |
| 199 | } | |
| 200 | var vecs: [8][]const u8 = undefined; // Arbitrarily chosen size. | |
| 201 | var i: usize = 1; | |
| 202 | vecs[0] = header; | |
| 203 | for (data[0 .. data.len - 1]) |buf| { | |
| 204 | if (buf.len == 0) continue; | |
| 205 | vecs[i] = buf; | |
| 206 | i += 1; | |
| 207 | if (vecs.len - i == 0) break; | |
| 194 | return writeSplatHeaderLimit(w, header, data, splat, .unlimited); | |
| 195 | } | |
| 196 | ||
| 197 | /// Equivalent to `writeSplatHeader` but writes at most `limit` bytes. | |
| 198 | pub fn writeSplatHeaderLimit( | |
| 199 | w: *Writer, | |
| 200 | header: []const u8, | |
| 201 | data: []const []const u8, | |
| 202 | splat: usize, | |
| 203 | limit: Limit, | |
| 204 | ) Error!usize { | |
| 205 | var remaining = @intFromEnum(limit); | |
| 206 | { | |
| 207 | const copy_len = @min(header.len, w.buffer.len - w.end, remaining); | |
| 208 | if (header.len - copy_len != 0) return writeSplatHeaderLimitFinish(w, header, data, splat, remaining); | |
| 209 | @memcpy(w.buffer[w.end..][0..copy_len], header[0..copy_len]); | |
| 210 | w.end += copy_len; | |
| 211 | remaining -= copy_len; | |
| 212 | } | |
| 213 | for (data[0 .. data.len - 1], 0..) |buf, i| { | |
| 214 | const copy_len = @min(buf.len, w.buffer.len - w.end, remaining); | |
| 215 | if (buf.len - copy_len != 0) return @intFromEnum(limit) - remaining + | |
| 216 | try writeSplatHeaderLimitFinish(w, &.{}, data[i..], splat, remaining); | |
| 217 | @memcpy(w.buffer[w.end..][0..copy_len], buf[0..copy_len]); | |
| 218 | w.end += copy_len; | |
| 219 | remaining -= copy_len; | |
| 208 | 220 | } |
| 209 | 221 | const pattern = data[data.len - 1]; |
| 210 | const new_splat = s: { | |
| 211 | if (pattern.len == 0 or vecs.len - i == 0) break :s 1; | |
| 222 | const splat_n = pattern.len * splat; | |
| 223 | if (splat_n > @min(w.buffer.len - w.end, remaining)) { | |
| 224 | const buffered_n = @intFromEnum(limit) - remaining; | |
| 225 | const written = try writeSplatHeaderLimitFinish(w, &.{}, data[data.len - 1 ..][0..1], splat, remaining); | |
| 226 | return buffered_n + written; | |
| 227 | } | |
| 228 | ||
| 229 | for (0..splat) |_| { | |
| 230 | @memcpy(w.buffer[w.end..][0..pattern.len], pattern); | |
| 231 | w.end += pattern.len; | |
| 232 | } | |
| 233 | ||
| 234 | remaining -= splat_n; | |
| 235 | return @intFromEnum(limit) - remaining; | |
| 236 | } | |
| 237 | ||
| 238 | fn writeSplatHeaderLimitFinish( | |
| 239 | w: *Writer, | |
| 240 | header: []const u8, | |
| 241 | data: []const []const u8, | |
| 242 | splat: usize, | |
| 243 | limit: usize, | |
| 244 | ) Error!usize { | |
| 245 | var remaining = limit; | |
| 246 | var vecs: [8][]const u8 = undefined; | |
| 247 | var i: usize = 0; | |
| 248 | v: { | |
| 249 | if (header.len != 0) { | |
| 250 | const copy_len = @min(header.len, remaining); | |
| 251 | vecs[i] = header[0..copy_len]; | |
| 252 | i += 1; | |
| 253 | remaining -= copy_len; | |
| 254 | if (remaining == 0) break :v; | |
| 255 | } | |
| 256 | for (data[0 .. data.len - 1]) |buf| if (buf.len != 0) { | |
| 257 | const copy_len = @min(header.len, remaining); | |
| 258 | vecs[i] = buf; | |
| 259 | i += 1; | |
| 260 | remaining -= copy_len; | |
| 261 | if (remaining == 0) break :v; | |
| 262 | if (vecs.len - i == 0) break :v; | |
| 263 | }; | |
| 264 | const pattern = data[data.len - 1]; | |
| 265 | if (splat == 1) { | |
| 266 | vecs[i] = pattern[0..@min(remaining, pattern.len)]; | |
| 267 | i += 1; | |
| 268 | break :v; | |
| 269 | } | |
| 212 | 270 | vecs[i] = pattern; |
| 213 | 271 | i += 1; |
| 214 | break :s splat; | |
| 215 | }; | |
| 216 | return w.vtable.drain(w, vecs[0..i], new_splat); | |
| 272 | return w.vtable.drain(w, (&vecs)[0..i], @min(remaining / pattern.len, splat)); | |
| 273 | } | |
| 274 | return w.vtable.drain(w, (&vecs)[0..i], 1); | |
| 217 | 275 | } |
| 218 | 276 | |
| 219 | 277 | test "writeSplatHeader splatting avoids buffer aliasing temptation" { |
lib/std/Io/test.zig+3-3| ... | ... | @@ -45,9 +45,9 @@ test "write a file, read it, then delete it" { |
| 45 | 45 | const expected_file_size: u64 = "begin".len + data.len + "end".len; |
| 46 | 46 | try expectEqual(expected_file_size, file_size); |
| 47 | 47 | |
| 48 | var buf_stream = io.bufferedReader(file.deprecatedReader()); | |
| 49 | const st = buf_stream.reader(); | |
| 50 | const contents = try st.readAllAlloc(std.testing.allocator, 2 * 1024); | |
| 48 | var file_buffer: [1024]u8 = undefined; | |
| 49 | var file_reader = file.reader(&file_buffer); | |
| 50 | const contents = try file_reader.interface.allocRemaining(std.testing.allocator, .limited(2 * 1024)); | |
| 51 | 51 | defer std.testing.allocator.free(contents); |
| 52 | 52 | |
| 53 | 53 | try expect(mem.eql(u8, contents[0.."begin".len], "begin")); |
lib/std/Progress.zig+1-1| ... | ... | @@ -1006,7 +1006,7 @@ fn serializeIpc(start_serialized_len: usize, serialized_buffer: *Serialized.Buff |
| 1006 | 1006 | continue; |
| 1007 | 1007 | } |
| 1008 | 1008 | const src = pipe_buf[m.remaining_read_trash_bytes..n]; |
| 1009 | std.mem.copyForwards(u8, &pipe_buf, src); | |
| 1009 | @memmove(pipe_buf[0..src.len], src); | |
| 1010 | 1010 | m.remaining_read_trash_bytes = 0; |
| 1011 | 1011 | bytes_read = src.len; |
| 1012 | 1012 | continue; |
lib/std/Target.zig+69-65| ... | ... | @@ -405,7 +405,7 @@ pub const Os = struct { |
| 405 | 405 | .fuchsia => .{ |
| 406 | 406 | .semver = .{ |
| 407 | 407 | .min = .{ .major = 1, .minor = 0, .patch = 0 }, |
| 408 | .max = .{ .major = 26, .minor = 0, .patch = 0 }, | |
| 408 | .max = .{ .major = 27, .minor = 0, .patch = 0 }, | |
| 409 | 409 | }, |
| 410 | 410 | }, |
| 411 | 411 | .hermit => .{ |
| ... | ... | @@ -446,7 +446,7 @@ pub const Os = struct { |
| 446 | 446 | |
| 447 | 447 | break :blk default_min; |
| 448 | 448 | }, |
| 449 | .max = .{ .major = 6, .minor = 13, .patch = 4 }, | |
| 449 | .max = .{ .major = 6, .minor = 16, .patch = 0 }, | |
| 450 | 450 | }, |
| 451 | 451 | .glibc = blk: { |
| 452 | 452 | // For 32-bit targets that traditionally used 32-bit time, we require |
| ... | ... | @@ -519,7 +519,7 @@ pub const Os = struct { |
| 519 | 519 | |
| 520 | 520 | break :blk default_min; |
| 521 | 521 | }, |
| 522 | .max = .{ .major = 14, .minor = 2, .patch = 0 }, | |
| 522 | .max = .{ .major = 14, .minor = 3, .patch = 0 }, | |
| 523 | 523 | }, |
| 524 | 524 | }, |
| 525 | 525 | .netbsd => .{ |
| ... | ... | @@ -549,38 +549,38 @@ pub const Os = struct { |
| 549 | 549 | |
| 550 | 550 | .driverkit => .{ |
| 551 | 551 | .semver = .{ |
| 552 | .min = .{ .major = 19, .minor = 0, .patch = 0 }, | |
| 553 | .max = .{ .major = 24, .minor = 4, .patch = 0 }, | |
| 552 | .min = .{ .major = 20, .minor = 0, .patch = 0 }, | |
| 553 | .max = .{ .major = 25, .minor = 0, .patch = 0 }, | |
| 554 | 554 | }, |
| 555 | 555 | }, |
| 556 | 556 | .macos => .{ |
| 557 | 557 | .semver = .{ |
| 558 | 558 | .min = .{ .major = 13, .minor = 0, .patch = 0 }, |
| 559 | .max = .{ .major = 15, .minor = 4, .patch = 1 }, | |
| 559 | .max = .{ .major = 15, .minor = 6, .patch = 0 }, | |
| 560 | 560 | }, |
| 561 | 561 | }, |
| 562 | 562 | .ios => .{ |
| 563 | 563 | .semver = .{ |
| 564 | 564 | .min = .{ .major = 15, .minor = 0, .patch = 0 }, |
| 565 | .max = .{ .major = 18, .minor = 4, .patch = 1 }, | |
| 565 | .max = .{ .major = 18, .minor = 6, .patch = 0 }, | |
| 566 | 566 | }, |
| 567 | 567 | }, |
| 568 | 568 | .tvos => .{ |
| 569 | 569 | .semver = .{ |
| 570 | 570 | .min = .{ .major = 15, .minor = 0, .patch = 0 }, |
| 571 | .max = .{ .major = 18, .minor = 4, .patch = 1 }, | |
| 571 | .max = .{ .major = 18, .minor = 5, .patch = 0 }, | |
| 572 | 572 | }, |
| 573 | 573 | }, |
| 574 | 574 | .visionos => .{ |
| 575 | 575 | .semver = .{ |
| 576 | 576 | .min = .{ .major = 1, .minor = 0, .patch = 0 }, |
| 577 | .max = .{ .major = 2, .minor = 4, .patch = 1 }, | |
| 577 | .max = .{ .major = 2, .minor = 5, .patch = 0 }, | |
| 578 | 578 | }, |
| 579 | 579 | }, |
| 580 | 580 | .watchos => .{ |
| 581 | 581 | .semver = .{ |
| 582 | .min = .{ .major = 7, .minor = 0, .patch = 0 }, | |
| 583 | .max = .{ .major = 11, .minor = 4, .patch = 0 }, | |
| 582 | .min = .{ .major = 8, .minor = 0, .patch = 0 }, | |
| 583 | .max = .{ .major = 11, .minor = 6, .patch = 0 }, | |
| 584 | 584 | }, |
| 585 | 585 | }, |
| 586 | 586 | |
| ... | ... | @@ -614,7 +614,7 @@ pub const Os = struct { |
| 614 | 614 | .amdhsa => .{ |
| 615 | 615 | .semver = .{ |
| 616 | 616 | .min = .{ .major = 5, .minor = 0, .patch = 0 }, |
| 617 | .max = .{ .major = 6, .minor = 4, .patch = 0 }, | |
| 617 | .max = .{ .major = 6, .minor = 4, .patch = 2 }, | |
| 618 | 618 | }, |
| 619 | 619 | }, |
| 620 | 620 | .amdpal => .{ |
| ... | ... | @@ -626,7 +626,7 @@ pub const Os = struct { |
| 626 | 626 | .cuda => .{ |
| 627 | 627 | .semver = .{ |
| 628 | 628 | .min = .{ .major = 11, .minor = 0, .patch = 1 }, |
| 629 | .max = .{ .major = 12, .minor = 9, .patch = 0 }, | |
| 629 | .max = .{ .major = 12, .minor = 9, .patch = 1 }, | |
| 630 | 630 | }, |
| 631 | 631 | }, |
| 632 | 632 | .nvcl, |
| ... | ... | @@ -646,7 +646,7 @@ pub const Os = struct { |
| 646 | 646 | .vulkan => .{ |
| 647 | 647 | .semver = .{ |
| 648 | 648 | .min = .{ .major = 1, .minor = 2, .patch = 0 }, |
| 649 | .max = .{ .major = 1, .minor = 4, .patch = 313 }, | |
| 649 | .max = .{ .major = 1, .minor = 4, .patch = 321 }, | |
| 650 | 650 | }, |
| 651 | 651 | }, |
| 652 | 652 | }; |
| ... | ... | @@ -697,57 +697,6 @@ pub const Os = struct { |
| 697 | 697 | => |field| @field(os.version_range, @tagName(field)).isAtLeast(ver), |
| 698 | 698 | }; |
| 699 | 699 | } |
| 700 | ||
| 701 | /// On Darwin, we always link libSystem which contains libc. | |
| 702 | /// Similarly on FreeBSD and NetBSD we always link system libc | |
| 703 | /// since this is the stable syscall interface. | |
| 704 | pub fn requiresLibC(os: Os) bool { | |
| 705 | return switch (os.tag) { | |
| 706 | .aix, | |
| 707 | .driverkit, | |
| 708 | .macos, | |
| 709 | .ios, | |
| 710 | .tvos, | |
| 711 | .watchos, | |
| 712 | .visionos, | |
| 713 | .dragonfly, | |
| 714 | .openbsd, | |
| 715 | .haiku, | |
| 716 | .solaris, | |
| 717 | .illumos, | |
| 718 | .serenity, | |
| 719 | => true, | |
| 720 | ||
| 721 | .linux, | |
| 722 | .windows, | |
| 723 | .freebsd, | |
| 724 | .netbsd, | |
| 725 | .freestanding, | |
| 726 | .fuchsia, | |
| 727 | .ps3, | |
| 728 | .zos, | |
| 729 | .rtems, | |
| 730 | .cuda, | |
| 731 | .nvcl, | |
| 732 | .amdhsa, | |
| 733 | .ps4, | |
| 734 | .ps5, | |
| 735 | .mesa3d, | |
| 736 | .contiki, | |
| 737 | .amdpal, | |
| 738 | .hermit, | |
| 739 | .hurd, | |
| 740 | .wasi, | |
| 741 | .emscripten, | |
| 742 | .uefi, | |
| 743 | .opencl, | |
| 744 | .opengl, | |
| 745 | .vulkan, | |
| 746 | .plan9, | |
| 747 | .other, | |
| 748 | => false, | |
| 749 | }; | |
| 750 | } | |
| 751 | 700 | }; |
| 752 | 701 | |
| 753 | 702 | pub const aarch64 = @import("Target/aarch64.zig"); |
| ... | ... | @@ -2055,6 +2004,61 @@ pub inline fn isWasiLibC(target: *const Target) bool { |
| 2055 | 2004 | return target.os.tag == .wasi and target.abi.isMusl(); |
| 2056 | 2005 | } |
| 2057 | 2006 | |
| 2007 | /// Does this target require linking libc? This may be the case if the target has an unstable | |
| 2008 | /// syscall interface, for example. | |
| 2009 | pub fn requiresLibC(target: *const Target) bool { | |
| 2010 | return switch (target.os.tag) { | |
| 2011 | .aix, | |
| 2012 | .driverkit, | |
| 2013 | .macos, | |
| 2014 | .ios, | |
| 2015 | .tvos, | |
| 2016 | .watchos, | |
| 2017 | .visionos, | |
| 2018 | .dragonfly, | |
| 2019 | .openbsd, | |
| 2020 | .haiku, | |
| 2021 | .solaris, | |
| 2022 | .illumos, | |
| 2023 | .serenity, | |
| 2024 | => true, | |
| 2025 | ||
| 2026 | // Android API levels prior to 29 did not have native TLS support. For these API levels, TLS | |
| 2027 | // is implemented through calls to `__emutls_get_address`. We provide this function in | |
| 2028 | // compiler-rt, but it's implemented by way of `pthread_key_create` et al, so linking libc | |
| 2029 | // is required. | |
| 2030 | .linux => target.abi.isAndroid() and target.os.version_range.linux.android < 29, | |
| 2031 | ||
| 2032 | .windows, | |
| 2033 | .freebsd, | |
| 2034 | .netbsd, | |
| 2035 | .freestanding, | |
| 2036 | .fuchsia, | |
| 2037 | .ps3, | |
| 2038 | .zos, | |
| 2039 | .rtems, | |
| 2040 | .cuda, | |
| 2041 | .nvcl, | |
| 2042 | .amdhsa, | |
| 2043 | .ps4, | |
| 2044 | .ps5, | |
| 2045 | .mesa3d, | |
| 2046 | .contiki, | |
| 2047 | .amdpal, | |
| 2048 | .hermit, | |
| 2049 | .hurd, | |
| 2050 | .wasi, | |
| 2051 | .emscripten, | |
| 2052 | .uefi, | |
| 2053 | .opencl, | |
| 2054 | .opengl, | |
| 2055 | .vulkan, | |
| 2056 | .plan9, | |
| 2057 | .other, | |
| 2058 | => false, | |
| 2059 | }; | |
| 2060 | } | |
| 2061 | ||
| 2058 | 2062 | pub const DynamicLinker = struct { |
| 2059 | 2063 | /// Contains the memory used to store the dynamic linker path. This field |
| 2060 | 2064 | /// should not be used directly. See `get` and `set`. This field exists so |
lib/std/Target/Query.zig+18-2| ... | ... | @@ -423,7 +423,7 @@ pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 { |
| 423 | 423 | try formatVersion(v, gpa, &result); |
| 424 | 424 | }, |
| 425 | 425 | .windows => |v| { |
| 426 | try result.print(gpa, "{d}", .{v}); | |
| 426 | try result.print(gpa, "{f}", .{v}); | |
| 427 | 427 | }, |
| 428 | 428 | } |
| 429 | 429 | } |
| ... | ... | @@ -437,7 +437,7 @@ pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 { |
| 437 | 437 | .windows => |v| { |
| 438 | 438 | // This is counting on a custom format() function defined on `WindowsVersion` |
| 439 | 439 | // to add a prefix '.' and make there be a total of three dots. |
| 440 | try result.print(gpa, "..{d}", .{v}); | |
| 440 | try result.print(gpa, "..{f}", .{v}); | |
| 441 | 441 | }, |
| 442 | 442 | } |
| 443 | 443 | } |
| ... | ... | @@ -729,4 +729,20 @@ test parse { |
| 729 | 729 | defer std.testing.allocator.free(text); |
| 730 | 730 | try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-android.30", text); |
| 731 | 731 | } |
| 732 | { | |
| 733 | const query = try Query.parse(.{ | |
| 734 | .arch_os_abi = "x86-windows.xp...win8-msvc", | |
| 735 | }); | |
| 736 | const target = try std.zig.system.resolveTargetQuery(query); | |
| 737 | ||
| 738 | try std.testing.expect(target.cpu.arch == .x86); | |
| 739 | try std.testing.expect(target.os.tag == .windows); | |
| 740 | try std.testing.expect(target.os.version_range.windows.min == .xp); | |
| 741 | try std.testing.expect(target.os.version_range.windows.max == .win8); | |
| 742 | try std.testing.expect(target.abi == .msvc); | |
| 743 | ||
| 744 | const text = try query.zigTriple(std.testing.allocator); | |
| 745 | defer std.testing.allocator.free(text); | |
| 746 | try std.testing.expectEqualSlices(u8, "x86-windows.xp...win8-msvc", text); | |
| 747 | } | |
| 732 | 748 | } |
lib/std/Uri.zig+2-1| ... | ... | @@ -377,7 +377,8 @@ pub fn parse(text: []const u8) ParseError!Uri { |
| 377 | 377 | |
| 378 | 378 | pub const ResolveInPlaceError = ParseError || error{NoSpaceLeft}; |
| 379 | 379 | |
| 380 | /// Resolves a URI against a base URI, conforming to RFC 3986, Section 5. | |
| 380 | /// Resolves a URI against a base URI, conforming to | |
| 381 | /// [RFC 3986, Section 5](https://www.rfc-editor.org/rfc/rfc3986#section-5) | |
| 381 | 382 | /// |
| 382 | 383 | /// Assumes new location is already copied to the beginning of `aux_buf.*`. |
| 383 | 384 | /// Parses that new location as a URI, and then resolves the path in place. |
lib/std/array_list.zig+174-16| ... | ... | @@ -158,7 +158,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty |
| 158 | 158 | assert(self.items.len < self.capacity); |
| 159 | 159 | self.items.len += 1; |
| 160 | 160 | |
| 161 | mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]); | |
| 161 | @memmove(self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]); | |
| 162 | 162 | self.items[i] = item; |
| 163 | 163 | } |
| 164 | 164 | |
| ... | ... | @@ -216,7 +216,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty |
| 216 | 216 | assert(self.capacity >= new_len); |
| 217 | 217 | const to_move = self.items[index..]; |
| 218 | 218 | self.items.len = new_len; |
| 219 | mem.copyBackwards(T, self.items[index + count ..], to_move); | |
| 219 | @memmove(self.items[index + count ..][0..to_move.len], to_move); | |
| 220 | 220 | const result = self.items[index..][0..count]; |
| 221 | 221 | @memset(result, undefined); |
| 222 | 222 | return result; |
| ... | ... | @@ -624,6 +624,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 624 | 624 | |
| 625 | 625 | /// Initialize with externally-managed memory. The buffer determines the |
| 626 | 626 | /// capacity, and the length is set to zero. |
| 627 | /// | |
| 627 | 628 | /// When initialized this way, all functions that accept an Allocator |
| 628 | 629 | /// argument cause illegal behavior. |
| 629 | 630 | pub fn initBuffer(buffer: Slice) Self { |
| ... | ... | @@ -705,18 +706,37 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 705 | 706 | } |
| 706 | 707 | |
| 707 | 708 | /// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room. |
| 708 | /// If in` is equal to the length of the list this operation is equivalent to append. | |
| 709 | /// | |
| 710 | /// If `i` is equal to the length of the list this operation is equivalent to append. | |
| 711 | /// | |
| 709 | 712 | /// This operation is O(N). |
| 713 | /// | |
| 710 | 714 | /// Asserts that the list has capacity for one additional item. |
| 715 | /// | |
| 711 | 716 | /// Asserts that the index is in bounds or equal to the length. |
| 712 | 717 | pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void { |
| 713 | 718 | assert(self.items.len < self.capacity); |
| 714 | 719 | self.items.len += 1; |
| 715 | 720 | |
| 716 | mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]); | |
| 721 | @memmove(self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]); | |
| 717 | 722 | self.items[i] = item; |
| 718 | 723 | } |
| 719 | 724 | |
| 725 | /// Insert `item` at index `i`, moving `list[i .. list.len]` to higher indices to make room. | |
| 726 | /// | |
| 727 | /// If `i` is equal to the length of the list this operation is equivalent to append. | |
| 728 | /// | |
| 729 | /// This operation is O(N). | |
| 730 | /// | |
| 731 | /// If the list lacks unused capacity for the additional item, returns | |
| 732 | /// `error.OutOfMemory`. | |
| 733 | /// | |
| 734 | /// Asserts that the index is in bounds or equal to the length. | |
| 735 | pub fn insertBounded(self: *Self, i: usize, item: T) error{OutOfMemory}!void { | |
| 736 | if (self.capacity - self.items.len == 0) return error.OutOfMemory; | |
| 737 | return insertAssumeCapacity(self, i, item); | |
| 738 | } | |
| 739 | ||
| 720 | 740 | /// Add `count` new elements at position `index`, which have |
| 721 | 741 | /// `undefined` values. Returns a slice pointing to the newly allocated |
| 722 | 742 | /// elements, which becomes invalid after various `ArrayList` |
| ... | ... | @@ -749,12 +769,29 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 749 | 769 | assert(self.capacity >= new_len); |
| 750 | 770 | const to_move = self.items[index..]; |
| 751 | 771 | self.items.len = new_len; |
| 752 | mem.copyBackwards(T, self.items[index + count ..], to_move); | |
| 772 | @memmove(self.items[index + count ..][0..to_move.len], to_move); | |
| 753 | 773 | const result = self.items[index..][0..count]; |
| 754 | 774 | @memset(result, undefined); |
| 755 | 775 | return result; |
| 756 | 776 | } |
| 757 | 777 | |
| 778 | /// Add `count` new elements at position `index`, which have | |
| 779 | /// `undefined` values, returning a slice pointing to the newly | |
| 780 | /// allocated elements, which becomes invalid after various `ArrayList` | |
| 781 | /// operations. | |
| 782 | /// | |
| 783 | /// Invalidates pre-existing pointers to elements at and after `index`, but | |
| 784 | /// does not invalidate any before that. | |
| 785 | /// | |
| 786 | /// If the list lacks unused capacity for the additional items, returns | |
| 787 | /// `error.OutOfMemory`. | |
| 788 | /// | |
| 789 | /// Asserts that the index is in bounds or equal to the length. | |
| 790 | pub fn addManyAtBounded(self: *Self, index: usize, count: usize) error{OutOfMemory}![]T { | |
| 791 | if (self.capacity - self.items.len < count) return error.OutOfMemory; | |
| 792 | return addManyAtAssumeCapacity(self, index, count); | |
| 793 | } | |
| 794 | ||
| 758 | 795 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 759 | 796 | /// This operation is O(N). |
| 760 | 797 | /// Invalidates pre-existing pointers to elements at and after `index`. |
| ... | ... | @@ -798,7 +835,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 798 | 835 | } |
| 799 | 836 | |
| 800 | 837 | /// Grows or shrinks the list as necessary. |
| 838 | /// | |
| 801 | 839 | /// Never invalidates element pointers. |
| 840 | /// | |
| 802 | 841 | /// Asserts the capacity is enough for additional items. |
| 803 | 842 | pub fn replaceRangeAssumeCapacity(self: *Self, start: usize, len: usize, new_items: []const T) void { |
| 804 | 843 | const after_range = start + len; |
| ... | ... | @@ -815,16 +854,24 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 815 | 854 | } else { |
| 816 | 855 | const extra = range.len - new_items.len; |
| 817 | 856 | @memcpy(range[0..new_items.len], new_items); |
| 818 | std.mem.copyForwards( | |
| 819 | T, | |
| 820 | self.items[after_range - extra ..], | |
| 821 | self.items[after_range..], | |
| 822 | ); | |
| 857 | const src = self.items[after_range..]; | |
| 858 | @memmove(self.items[after_range - extra ..][0..src.len], src); | |
| 823 | 859 | @memset(self.items[self.items.len - extra ..], undefined); |
| 824 | 860 | self.items.len -= extra; |
| 825 | 861 | } |
| 826 | 862 | } |
| 827 | 863 | |
| 864 | /// Grows or shrinks the list as necessary. | |
| 865 | /// | |
| 866 | /// Never invalidates element pointers. | |
| 867 | /// | |
| 868 | /// If the unused capacity is insufficient for additional items, | |
| 869 | /// returns `error.OutOfMemory`. | |
| 870 | pub fn replaceRangeBounded(self: *Self, start: usize, len: usize, new_items: []const T) error{OutOfMemory}!void { | |
| 871 | if (self.capacity - self.items.len < new_items.len -| len) return error.OutOfMemory; | |
| 872 | return replaceRangeAssumeCapacity(self, start, len, new_items); | |
| 873 | } | |
| 874 | ||
| 828 | 875 | /// Extend the list by 1 element. Allocates more memory as necessary. |
| 829 | 876 | /// Invalidates element pointers if additional memory is needed. |
| 830 | 877 | pub fn append(self: *Self, gpa: Allocator, item: T) Allocator.Error!void { |
| ... | ... | @@ -833,12 +880,25 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 833 | 880 | } |
| 834 | 881 | |
| 835 | 882 | /// Extend the list by 1 element. |
| 883 | /// | |
| 836 | 884 | /// Never invalidates element pointers. |
| 885 | /// | |
| 837 | 886 | /// Asserts that the list can hold one additional item. |
| 838 | 887 | pub fn appendAssumeCapacity(self: *Self, item: T) void { |
| 839 | 888 | self.addOneAssumeCapacity().* = item; |
| 840 | 889 | } |
| 841 | 890 | |
| 891 | /// Extend the list by 1 element. | |
| 892 | /// | |
| 893 | /// Never invalidates element pointers. | |
| 894 | /// | |
| 895 | /// If the list lacks unused capacity for the additional item, returns | |
| 896 | /// `error.OutOfMemory`. | |
| 897 | pub fn appendBounded(self: *Self, item: T) error{OutOfMemory}!void { | |
| 898 | if (self.capacity - self.items.len == 0) return error.OutOfMemory; | |
| 899 | return appendAssumeCapacity(self, item); | |
| 900 | } | |
| 901 | ||
| 842 | 902 | /// Remove the element at index `i` from the list and return its value. |
| 843 | 903 | /// Invalidates pointers to the last element. |
| 844 | 904 | /// This operation is O(N). |
| ... | ... | @@ -873,6 +933,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 873 | 933 | } |
| 874 | 934 | |
| 875 | 935 | /// Append the slice of items to the list. |
| 936 | /// | |
| 876 | 937 | /// Asserts that the list can hold the additional items. |
| 877 | 938 | pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void { |
| 878 | 939 | const old_len = self.items.len; |
| ... | ... | @@ -882,6 +943,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 882 | 943 | @memcpy(self.items[old_len..][0..items.len], items); |
| 883 | 944 | } |
| 884 | 945 | |
| 946 | /// Append the slice of items to the list. | |
| 947 | /// | |
| 948 | /// If the list lacks unused capacity for the additional items, returns `error.OutOfMemory`. | |
| 949 | pub fn appendSliceBounded(self: *Self, items: []const T) error{OutOfMemory}!void { | |
| 950 | if (self.capacity - self.items.len < items.len) return error.OutOfMemory; | |
| 951 | return appendSliceAssumeCapacity(self, items); | |
| 952 | } | |
| 953 | ||
| 885 | 954 | /// Append the slice of items to the list. Allocates more |
| 886 | 955 | /// memory as necessary. Only call this function if a call to `appendSlice` instead would |
| 887 | 956 | /// be a compile error. |
| ... | ... | @@ -892,8 +961,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 892 | 961 | } |
| 893 | 962 | |
| 894 | 963 | /// Append an unaligned slice of items to the list. |
| 895 | /// Only call this function if a call to `appendSliceAssumeCapacity` | |
| 896 | /// instead would be a compile error. | |
| 964 | /// | |
| 965 | /// Intended to be used only when `appendSliceAssumeCapacity` would be | |
| 966 | /// a compile error. | |
| 967 | /// | |
| 897 | 968 | /// Asserts that the list can hold the additional items. |
| 898 | 969 | pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void { |
| 899 | 970 | const old_len = self.items.len; |
| ... | ... | @@ -903,6 +974,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 903 | 974 | @memcpy(self.items[old_len..][0..items.len], items); |
| 904 | 975 | } |
| 905 | 976 | |
| 977 | /// Append an unaligned slice of items to the list. | |
| 978 | /// | |
| 979 | /// Intended to be used only when `appendSliceAssumeCapacity` would be | |
| 980 | /// a compile error. | |
| 981 | /// | |
| 982 | /// If the list lacks unused capacity for the additional items, returns | |
| 983 | /// `error.OutOfMemory`. | |
| 984 | pub fn appendUnalignedSliceBounded(self: *Self, items: []align(1) const T) error{OutOfMemory}!void { | |
| 985 | if (self.capacity - self.items.len < items.len) return error.OutOfMemory; | |
| 986 | return appendUnalignedSliceAssumeCapacity(self, items); | |
| 987 | } | |
| 988 | ||
| 906 | 989 | pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { |
| 907 | 990 | comptime assert(T == u8); |
| 908 | 991 | try self.ensureUnusedCapacity(gpa, fmt.len); |
| ... | ... | @@ -920,6 +1003,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 920 | 1003 | self.items.len += w.end; |
| 921 | 1004 | } |
| 922 | 1005 | |
| 1006 | pub fn printBounded(self: *Self, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void { | |
| 1007 | comptime assert(T == u8); | |
| 1008 | var w: std.io.Writer = .fixed(self.unusedCapacitySlice()); | |
| 1009 | w.print(fmt, args) catch return error.OutOfMemory; | |
| 1010 | self.items.len += w.end; | |
| 1011 | } | |
| 1012 | ||
| 923 | 1013 | /// Append a value to the list `n` times. |
| 924 | 1014 | /// Allocates more memory as necessary. |
| 925 | 1015 | /// Invalidates element pointers if additional memory is needed. |
| ... | ... | @@ -932,9 +1022,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 932 | 1022 | } |
| 933 | 1023 | |
| 934 | 1024 | /// Append a value to the list `n` times. |
| 1025 | /// | |
| 935 | 1026 | /// Never invalidates element pointers. |
| 1027 | /// | |
| 936 | 1028 | /// The function is inline so that a comptime-known `value` parameter will |
| 937 | 1029 | /// have better memset codegen in case it has a repeated byte pattern. |
| 1030 | /// | |
| 938 | 1031 | /// Asserts that the list can hold the additional items. |
| 939 | 1032 | pub inline fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { |
| 940 | 1033 | const new_len = self.items.len + n; |
| ... | ... | @@ -943,6 +1036,22 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 943 | 1036 | self.items.len = new_len; |
| 944 | 1037 | } |
| 945 | 1038 | |
| 1039 | /// Append a value to the list `n` times. | |
| 1040 | /// | |
| 1041 | /// Never invalidates element pointers. | |
| 1042 | /// | |
| 1043 | /// The function is inline so that a comptime-known `value` parameter will | |
| 1044 | /// have better memset codegen in case it has a repeated byte pattern. | |
| 1045 | /// | |
| 1046 | /// If the list lacks unused capacity for the additional items, returns | |
| 1047 | /// `error.OutOfMemory`. | |
| 1048 | pub inline fn appendNTimesBounded(self: *Self, value: T, n: usize) error{OutOfMemory}!void { | |
| 1049 | const new_len = self.items.len + n; | |
| 1050 | if (self.capacity < new_len) return error.OutOfMemory; | |
| 1051 | @memset(self.items.ptr[self.items.len..new_len], value); | |
| 1052 | self.items.len = new_len; | |
| 1053 | } | |
| 1054 | ||
| 946 | 1055 | /// Adjust the list length to `new_len`. |
| 947 | 1056 | /// Additional elements contain the value `undefined`. |
| 948 | 1057 | /// Invalidates element pointers if additional memory is needed. |
| ... | ... | @@ -1068,8 +1177,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 1068 | 1177 | } |
| 1069 | 1178 | |
| 1070 | 1179 | /// Increase length by 1, returning pointer to the new item. |
| 1180 | /// | |
| 1071 | 1181 | /// Never invalidates element pointers. |
| 1182 | /// | |
| 1072 | 1183 | /// The returned element pointer becomes invalid when the list is resized. |
| 1184 | /// | |
| 1073 | 1185 | /// Asserts that the list can hold one additional item. |
| 1074 | 1186 | pub fn addOneAssumeCapacity(self: *Self) *T { |
| 1075 | 1187 | assert(self.items.len < self.capacity); |
| ... | ... | @@ -1078,6 +1190,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 1078 | 1190 | return &self.items[self.items.len - 1]; |
| 1079 | 1191 | } |
| 1080 | 1192 | |
| 1193 | /// Increase length by 1, returning pointer to the new item. | |
| 1194 | /// | |
| 1195 | /// Never invalidates element pointers. | |
| 1196 | /// | |
| 1197 | /// The returned element pointer becomes invalid when the list is resized. | |
| 1198 | /// | |
| 1199 | /// If the list lacks unused capacity for the additional item, returns `error.OutOfMemory`. | |
| 1200 | pub fn addOneBounded(self: *Self) error{OutOfMemory}!*T { | |
| 1201 | if (self.capacity - self.items.len < 1) return error.OutOfMemory; | |
| 1202 | return addOneAssumeCapacity(self); | |
| 1203 | } | |
| 1204 | ||
| 1081 | 1205 | /// Resize the array, adding `n` new elements, which have `undefined` values. |
| 1082 | 1206 | /// The return value is an array pointing to the newly allocated elements. |
| 1083 | 1207 | /// The returned pointer becomes invalid when the list is resized. |
| ... | ... | @@ -1088,9 +1212,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 1088 | 1212 | } |
| 1089 | 1213 | |
| 1090 | 1214 | /// Resize the array, adding `n` new elements, which have `undefined` values. |
| 1215 | /// | |
| 1091 | 1216 | /// The return value is an array pointing to the newly allocated elements. |
| 1217 | /// | |
| 1092 | 1218 | /// Never invalidates element pointers. |
| 1219 | /// | |
| 1093 | 1220 | /// The returned pointer becomes invalid when the list is resized. |
| 1221 | /// | |
| 1094 | 1222 | /// Asserts that the list can hold the additional items. |
| 1095 | 1223 | pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T { |
| 1096 | 1224 | assert(self.items.len + n <= self.capacity); |
| ... | ... | @@ -1099,6 +1227,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 1099 | 1227 | return self.items[prev_len..][0..n]; |
| 1100 | 1228 | } |
| 1101 | 1229 | |
| 1230 | /// Resize the array, adding `n` new elements, which have `undefined` values. | |
| 1231 | /// | |
| 1232 | /// The return value is an array pointing to the newly allocated elements. | |
| 1233 | /// | |
| 1234 | /// Never invalidates element pointers. | |
| 1235 | /// | |
| 1236 | /// The returned pointer becomes invalid when the list is resized. | |
| 1237 | /// | |
| 1238 | /// If the list lacks unused capacity for the additional items, returns | |
| 1239 | /// `error.OutOfMemory`. | |
| 1240 | pub fn addManyAsArrayBounded(self: *Self, comptime n: usize) error{OutOfMemory}!*[n]T { | |
| 1241 | if (self.capacity - self.items.len < n) return error.OutOfMemory; | |
| 1242 | return addManyAsArrayAssumeCapacity(self, n); | |
| 1243 | } | |
| 1244 | ||
| 1102 | 1245 | /// Resize the array, adding `n` new elements, which have `undefined` values. |
| 1103 | 1246 | /// The return value is a slice pointing to the newly allocated elements. |
| 1104 | 1247 | /// The returned pointer becomes invalid when the list is resized. |
| ... | ... | @@ -1109,10 +1252,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 1109 | 1252 | return self.items[prev_len..][0..n]; |
| 1110 | 1253 | } |
| 1111 | 1254 | |
| 1112 | /// Resize the array, adding `n` new elements, which have `undefined` values. | |
| 1113 | /// The return value is a slice pointing to the newly allocated elements. | |
| 1114 | /// Never invalidates element pointers. | |
| 1115 | /// The returned pointer becomes invalid when the list is resized. | |
| 1255 | /// Resizes the array, adding `n` new elements, which have `undefined` | |
| 1256 | /// values, returning a slice pointing to the newly allocated elements. | |
| 1257 | /// | |
| 1258 | /// Never invalidates element pointers. The returned pointer becomes | |
| 1259 | /// invalid when the list is resized. | |
| 1260 | /// | |
| 1116 | 1261 | /// Asserts that the list can hold the additional items. |
| 1117 | 1262 | pub fn addManyAsSliceAssumeCapacity(self: *Self, n: usize) []T { |
| 1118 | 1263 | assert(self.items.len + n <= self.capacity); |
| ... | ... | @@ -1121,6 +1266,19 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig |
| 1121 | 1266 | return self.items[prev_len..][0..n]; |
| 1122 | 1267 | } |
| 1123 | 1268 | |
| 1269 | /// Resizes the array, adding `n` new elements, which have `undefined` | |
| 1270 | /// values, returning a slice pointing to the newly allocated elements. | |
| 1271 | /// | |
| 1272 | /// Never invalidates element pointers. The returned pointer becomes | |
| 1273 | /// invalid when the list is resized. | |
| 1274 | /// | |
| 1275 | /// If the list lacks unused capacity for the additional items, returns | |
| 1276 | /// `error.OutOfMemory`. | |
| 1277 | pub fn addManyAsSliceBounded(self: *Self, n: usize) error{OutOfMemory}![]T { | |
| 1278 | if (self.capacity - self.items.len < n) return error.OutOfMemory; | |
| 1279 | return addManyAsSliceAssumeCapacity(self, n); | |
| 1280 | } | |
| 1281 | ||
| 1124 | 1282 | /// Remove and return the last element from the list. |
| 1125 | 1283 | /// If the list is empty, returns `null`. |
| 1126 | 1284 | /// Invalidates pointers to last element. |
lib/std/base64.zig+6-26| ... | ... | @@ -118,22 +118,6 @@ pub const Base64Encoder = struct { |
| 118 | 118 | } |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | // destWriter must be compatible with std.io.GenericWriter's writeAll interface | |
| 122 | // sourceReader must be compatible with `std.io.GenericReader` read interface | |
| 123 | pub fn encodeFromReaderToWriter(encoder: *const Base64Encoder, destWriter: anytype, sourceReader: anytype) !void { | |
| 124 | while (true) { | |
| 125 | var tempSource: [3]u8 = undefined; | |
| 126 | const bytesRead = try sourceReader.read(&tempSource); | |
| 127 | if (bytesRead == 0) { | |
| 128 | break; | |
| 129 | } | |
| 130 | ||
| 131 | var temp: [5]u8 = undefined; | |
| 132 | const s = encoder.encode(&temp, tempSource[0..bytesRead]); | |
| 133 | try destWriter.writeAll(s); | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | 121 | /// dest.len must at least be what you get from ::calcSize. |
| 138 | 122 | pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 { |
| 139 | 123 | const out_len = encoder.calcSize(source.len); |
| ... | ... | @@ -517,17 +501,13 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [ |
| 517 | 501 | var buffer: [0x100]u8 = undefined; |
| 518 | 502 | const encoded = codecs.Encoder.encode(&buffer, expected_decoded); |
| 519 | 503 | try testing.expectEqualSlices(u8, expected_encoded, encoded); |
| 520 | ||
| 504 | } | |
| 505 | { | |
| 521 | 506 | // stream encode |
| 522 | var list = try std.BoundedArray(u8, 0x100).init(0); | |
| 523 | try codecs.Encoder.encodeWriter(list.writer(), expected_decoded); | |
| 524 | try testing.expectEqualSlices(u8, expected_encoded, list.slice()); | |
| 525 | ||
| 526 | // reader to writer encode | |
| 527 | var stream = std.io.fixedBufferStream(expected_decoded); | |
| 528 | list = try std.BoundedArray(u8, 0x100).init(0); | |
| 529 | try codecs.Encoder.encodeFromReaderToWriter(list.writer(), stream.reader()); | |
| 530 | try testing.expectEqualSlices(u8, expected_encoded, list.slice()); | |
| 507 | var buffer: [0x100]u8 = undefined; | |
| 508 | var writer: std.Io.Writer = .fixed(&buffer); | |
| 509 | try codecs.Encoder.encodeWriter(&writer, expected_decoded); | |
| 510 | try testing.expectEqualSlices(u8, expected_encoded, writer.buffered()); | |
| 531 | 511 | } |
| 532 | 512 | |
| 533 | 513 | // Base64Decoder |
lib/std/bounded_array.zig deleted-412| ... | ... | @@ -1,412 +0,0 @@ |
| 1 | const std = @import("std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const mem = std.mem; | |
| 4 | const testing = std.testing; | |
| 5 | const Alignment = std.mem.Alignment; | |
| 6 | ||
| 7 | /// A structure with an array and a length, that can be used as a slice. | |
| 8 | /// | |
| 9 | /// Useful to pass around small arrays whose exact size is only known at | |
| 10 | /// runtime, but whose maximum size is known at comptime, without requiring | |
| 11 | /// an `Allocator`. | |
| 12 | /// | |
| 13 | /// ```zig | |
| 14 | /// var actual_size = 32; | |
| 15 | /// var a = try BoundedArray(u8, 64).init(actual_size); | |
| 16 | /// var slice = a.slice(); // a slice of the 64-byte array | |
| 17 | /// var a_clone = a; // creates a copy - the structure doesn't use any internal pointers | |
| 18 | /// ``` | |
| 19 | pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { | |
| 20 | return BoundedArrayAligned(T, .of(T), buffer_capacity); | |
| 21 | } | |
| 22 | ||
| 23 | /// A structure with an array, length and alignment, that can be used as a | |
| 24 | /// slice. | |
| 25 | /// | |
| 26 | /// Useful to pass around small explicitly-aligned arrays whose exact size is | |
| 27 | /// only known at runtime, but whose maximum size is known at comptime, without | |
| 28 | /// requiring an `Allocator`. | |
| 29 | /// ```zig | |
| 30 | // var a = try BoundedArrayAligned(u8, 16, 2).init(0); | |
| 31 | // try a.append(255); | |
| 32 | // try a.append(255); | |
| 33 | // const b = @ptrCast(*const [1]u16, a.constSlice().ptr); | |
| 34 | // try testing.expectEqual(@as(u16, 65535), b[0]); | |
| 35 | /// ``` | |
| 36 | pub fn BoundedArrayAligned( | |
| 37 | comptime T: type, | |
| 38 | comptime alignment: Alignment, | |
| 39 | comptime buffer_capacity: usize, | |
| 40 | ) type { | |
| 41 | return struct { | |
| 42 | const Self = @This(); | |
| 43 | buffer: [buffer_capacity]T align(alignment.toByteUnits()) = undefined, | |
| 44 | len: usize = 0, | |
| 45 | ||
| 46 | /// Set the actual length of the slice. | |
| 47 | /// Returns error.Overflow if it exceeds the length of the backing array. | |
| 48 | pub fn init(len: usize) error{Overflow}!Self { | |
| 49 | if (len > buffer_capacity) return error.Overflow; | |
| 50 | return Self{ .len = len }; | |
| 51 | } | |
| 52 | ||
| 53 | /// View the internal array as a slice whose size was previously set. | |
| 54 | pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) { | |
| 55 | *align(alignment.toByteUnits()) [buffer_capacity]T => []align(alignment.toByteUnits()) T, | |
| 56 | *align(alignment.toByteUnits()) const [buffer_capacity]T => []align(alignment.toByteUnits()) const T, | |
| 57 | else => unreachable, | |
| 58 | } { | |
| 59 | return self.buffer[0..self.len]; | |
| 60 | } | |
| 61 | ||
| 62 | /// View the internal array as a constant slice whose size was previously set. | |
| 63 | pub fn constSlice(self: *const Self) []align(alignment.toByteUnits()) const T { | |
| 64 | return self.slice(); | |
| 65 | } | |
| 66 | ||
| 67 | /// Adjust the slice's length to `len`. | |
| 68 | /// Does not initialize added items if any. | |
| 69 | pub fn resize(self: *Self, len: usize) error{Overflow}!void { | |
| 70 | if (len > buffer_capacity) return error.Overflow; | |
| 71 | self.len = len; | |
| 72 | } | |
| 73 | ||
| 74 | /// Remove all elements from the slice. | |
| 75 | pub fn clear(self: *Self) void { | |
| 76 | self.len = 0; | |
| 77 | } | |
| 78 | ||
| 79 | /// Copy the content of an existing slice. | |
| 80 | pub fn fromSlice(m: []const T) error{Overflow}!Self { | |
| 81 | var list = try init(m.len); | |
| 82 | @memcpy(list.slice(), m); | |
| 83 | return list; | |
| 84 | } | |
| 85 | ||
| 86 | /// Return the element at index `i` of the slice. | |
| 87 | pub fn get(self: Self, i: usize) T { | |
| 88 | return self.constSlice()[i]; | |
| 89 | } | |
| 90 | ||
| 91 | /// Set the value of the element at index `i` of the slice. | |
| 92 | pub fn set(self: *Self, i: usize, item: T) void { | |
| 93 | self.slice()[i] = item; | |
| 94 | } | |
| 95 | ||
| 96 | /// Return the maximum length of a slice. | |
| 97 | pub fn capacity(self: Self) usize { | |
| 98 | return self.buffer.len; | |
| 99 | } | |
| 100 | ||
| 101 | /// Check that the slice can hold at least `additional_count` items. | |
| 102 | pub fn ensureUnusedCapacity(self: Self, additional_count: usize) error{Overflow}!void { | |
| 103 | if (self.len + additional_count > buffer_capacity) { | |
| 104 | return error.Overflow; | |
| 105 | } | |
| 106 | } | |
| 107 | ||
| 108 | /// Increase length by 1, returning a pointer to the new item. | |
| 109 | pub fn addOne(self: *Self) error{Overflow}!*T { | |
| 110 | try self.ensureUnusedCapacity(1); | |
| 111 | return self.addOneAssumeCapacity(); | |
| 112 | } | |
| 113 | ||
| 114 | /// Increase length by 1, returning pointer to the new item. | |
| 115 | /// Asserts that there is space for the new item. | |
| 116 | pub fn addOneAssumeCapacity(self: *Self) *T { | |
| 117 | assert(self.len < buffer_capacity); | |
| 118 | self.len += 1; | |
| 119 | return &self.slice()[self.len - 1]; | |
| 120 | } | |
| 121 | ||
| 122 | /// Resize the slice, adding `n` new elements, which have `undefined` values. | |
| 123 | /// The return value is a pointer to the array of uninitialized elements. | |
| 124 | pub fn addManyAsArray(self: *Self, comptime n: usize) error{Overflow}!*align(alignment.toByteUnits()) [n]T { | |
| 125 | const prev_len = self.len; | |
| 126 | try self.resize(self.len + n); | |
| 127 | return self.slice()[prev_len..][0..n]; | |
| 128 | } | |
| 129 | ||
| 130 | /// Resize the slice, adding `n` new elements, which have `undefined` values. | |
| 131 | /// The return value is a slice pointing to the uninitialized elements. | |
| 132 | pub fn addManyAsSlice(self: *Self, n: usize) error{Overflow}![]align(alignment.toByteUnits()) T { | |
| 133 | const prev_len = self.len; | |
| 134 | try self.resize(self.len + n); | |
| 135 | return self.slice()[prev_len..][0..n]; | |
| 136 | } | |
| 137 | ||
| 138 | /// Remove and return the last element from the slice, or return `null` if the slice is empty. | |
| 139 | pub fn pop(self: *Self) ?T { | |
| 140 | if (self.len == 0) return null; | |
| 141 | const item = self.get(self.len - 1); | |
| 142 | self.len -= 1; | |
| 143 | return item; | |
| 144 | } | |
| 145 | ||
| 146 | /// Return a slice of only the extra capacity after items. | |
| 147 | /// This can be useful for writing directly into it. | |
| 148 | /// Note that such an operation must be followed up with a | |
| 149 | /// call to `resize()` | |
| 150 | pub fn unusedCapacitySlice(self: *Self) []align(alignment.toByteUnits()) T { | |
| 151 | return self.buffer[self.len..]; | |
| 152 | } | |
| 153 | ||
| 154 | /// Insert `item` at index `i` by moving `slice[n .. slice.len]` to make room. | |
| 155 | /// This operation is O(N). | |
| 156 | pub fn insert( | |
| 157 | self: *Self, | |
| 158 | i: usize, | |
| 159 | item: T, | |
| 160 | ) error{Overflow}!void { | |
| 161 | if (i > self.len) { | |
| 162 | return error.Overflow; | |
| 163 | } | |
| 164 | _ = try self.addOne(); | |
| 165 | var s = self.slice(); | |
| 166 | mem.copyBackwards(T, s[i + 1 .. s.len], s[i .. s.len - 1]); | |
| 167 | self.buffer[i] = item; | |
| 168 | } | |
| 169 | ||
| 170 | /// Insert slice `items` at index `i` by moving `slice[i .. slice.len]` to make room. | |
| 171 | /// This operation is O(N). | |
| 172 | pub fn insertSlice(self: *Self, i: usize, items: []const T) error{Overflow}!void { | |
| 173 | try self.ensureUnusedCapacity(items.len); | |
| 174 | self.len += items.len; | |
| 175 | mem.copyBackwards(T, self.slice()[i + items.len .. self.len], self.constSlice()[i .. self.len - items.len]); | |
| 176 | @memcpy(self.slice()[i..][0..items.len], items); | |
| 177 | } | |
| 178 | ||
| 179 | /// Replace range of elements `slice[start..][0..len]` with `new_items`. | |
| 180 | /// Grows slice if `len < new_items.len`. | |
| 181 | /// Shrinks slice if `len > new_items.len`. | |
| 182 | pub fn replaceRange( | |
| 183 | self: *Self, | |
| 184 | start: usize, | |
| 185 | len: usize, | |
| 186 | new_items: []const T, | |
| 187 | ) error{Overflow}!void { | |
| 188 | const after_range = start + len; | |
| 189 | var range = self.slice()[start..after_range]; | |
| 190 | ||
| 191 | if (range.len == new_items.len) { | |
| 192 | @memcpy(range[0..new_items.len], new_items); | |
| 193 | } else if (range.len < new_items.len) { | |
| 194 | const first = new_items[0..range.len]; | |
| 195 | const rest = new_items[range.len..]; | |
| 196 | @memcpy(range[0..first.len], first); | |
| 197 | try self.insertSlice(after_range, rest); | |
| 198 | } else { | |
| 199 | @memcpy(range[0..new_items.len], new_items); | |
| 200 | const after_subrange = start + new_items.len; | |
| 201 | for (self.constSlice()[after_range..], 0..) |item, i| { | |
| 202 | self.slice()[after_subrange..][i] = item; | |
| 203 | } | |
| 204 | self.len -= len - new_items.len; | |
| 205 | } | |
| 206 | } | |
| 207 | ||
| 208 | /// Extend the slice by 1 element. | |
| 209 | pub fn append(self: *Self, item: T) error{Overflow}!void { | |
| 210 | const new_item_ptr = try self.addOne(); | |
| 211 | new_item_ptr.* = item; | |
| 212 | } | |
| 213 | ||
| 214 | /// Extend the slice by 1 element, asserting the capacity is already | |
| 215 | /// enough to store the new item. | |
| 216 | pub fn appendAssumeCapacity(self: *Self, item: T) void { | |
| 217 | const new_item_ptr = self.addOneAssumeCapacity(); | |
| 218 | new_item_ptr.* = item; | |
| 219 | } | |
| 220 | ||
| 221 | /// Remove the element at index `i`, shift elements after index | |
| 222 | /// `i` forward, and return the removed element. | |
| 223 | /// Asserts the slice has at least one item. | |
| 224 | /// This operation is O(N). | |
| 225 | pub fn orderedRemove(self: *Self, i: usize) T { | |
| 226 | const newlen = self.len - 1; | |
| 227 | if (newlen == i) return self.pop().?; | |
| 228 | const old_item = self.get(i); | |
| 229 | for (self.slice()[i..newlen], 0..) |*b, j| b.* = self.get(i + 1 + j); | |
| 230 | self.set(newlen, undefined); | |
| 231 | self.len = newlen; | |
| 232 | return old_item; | |
| 233 | } | |
| 234 | ||
| 235 | /// Remove the element at the specified index and return it. | |
| 236 | /// The empty slot is filled from the end of the slice. | |
| 237 | /// This operation is O(1). | |
| 238 | pub fn swapRemove(self: *Self, i: usize) T { | |
| 239 | if (self.len - 1 == i) return self.pop().?; | |
| 240 | const old_item = self.get(i); | |
| 241 | self.set(i, self.pop().?); | |
| 242 | return old_item; | |
| 243 | } | |
| 244 | ||
| 245 | /// Append the slice of items to the slice. | |
| 246 | pub fn appendSlice(self: *Self, items: []const T) error{Overflow}!void { | |
| 247 | try self.ensureUnusedCapacity(items.len); | |
| 248 | self.appendSliceAssumeCapacity(items); | |
| 249 | } | |
| 250 | ||
| 251 | /// Append the slice of items to the slice, asserting the capacity is already | |
| 252 | /// enough to store the new items. | |
| 253 | pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void { | |
| 254 | const old_len = self.len; | |
| 255 | self.len += items.len; | |
| 256 | @memcpy(self.slice()[old_len..][0..items.len], items); | |
| 257 | } | |
| 258 | ||
| 259 | /// Append a value to the slice `n` times. | |
| 260 | /// Allocates more memory as necessary. | |
| 261 | pub fn appendNTimes(self: *Self, value: T, n: usize) error{Overflow}!void { | |
| 262 | const old_len = self.len; | |
| 263 | try self.resize(old_len + n); | |
| 264 | @memset(self.slice()[old_len..self.len], value); | |
| 265 | } | |
| 266 | ||
| 267 | /// Append a value to the slice `n` times. | |
| 268 | /// Asserts the capacity is enough. | |
| 269 | pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { | |
| 270 | const old_len = self.len; | |
| 271 | self.len += n; | |
| 272 | assert(self.len <= buffer_capacity); | |
| 273 | @memset(self.slice()[old_len..self.len], value); | |
| 274 | } | |
| 275 | ||
| 276 | pub const Writer = if (T != u8) | |
| 277 | @compileError("The Writer interface is only defined for BoundedArray(u8, ...) " ++ | |
| 278 | "but the given type is BoundedArray(" ++ @typeName(T) ++ ", ...)") | |
| 279 | else | |
| 280 | std.io.GenericWriter(*Self, error{Overflow}, appendWrite); | |
| 281 | ||
| 282 | /// Initializes a writer which will write into the array. | |
| 283 | pub fn writer(self: *Self) Writer { | |
| 284 | return .{ .context = self }; | |
| 285 | } | |
| 286 | ||
| 287 | /// Same as `appendSlice` except it returns the number of bytes written, which is always the same | |
| 288 | /// as `m.len`. The purpose of this function existing is to match `std.io.GenericWriter` API. | |
| 289 | fn appendWrite(self: *Self, m: []const u8) error{Overflow}!usize { | |
| 290 | try self.appendSlice(m); | |
| 291 | return m.len; | |
| 292 | } | |
| 293 | }; | |
| 294 | } | |
| 295 | ||
| 296 | test BoundedArray { | |
| 297 | var a = try BoundedArray(u8, 64).init(32); | |
| 298 | ||
| 299 | try testing.expectEqual(a.capacity(), 64); | |
| 300 | try testing.expectEqual(a.slice().len, 32); | |
| 301 | try testing.expectEqual(a.constSlice().len, 32); | |
| 302 | ||
| 303 | try a.resize(48); | |
| 304 | try testing.expectEqual(a.len, 48); | |
| 305 | ||
| 306 | const x = [_]u8{1} ** 10; | |
| 307 | a = try BoundedArray(u8, 64).fromSlice(&x); | |
| 308 | try testing.expectEqualSlices(u8, &x, a.constSlice()); | |
| 309 | ||
| 310 | var a2 = a; | |
| 311 | try testing.expectEqualSlices(u8, a.constSlice(), a2.constSlice()); | |
| 312 | a2.set(0, 0); | |
| 313 | try testing.expect(a.get(0) != a2.get(0)); | |
| 314 | ||
| 315 | try testing.expectError(error.Overflow, a.resize(100)); | |
| 316 | try testing.expectError(error.Overflow, BoundedArray(u8, x.len - 1).fromSlice(&x)); | |
| 317 | ||
| 318 | try a.resize(0); | |
| 319 | try a.ensureUnusedCapacity(a.capacity()); | |
| 320 | (try a.addOne()).* = 0; | |
| 321 | try a.ensureUnusedCapacity(a.capacity() - 1); | |
| 322 | try testing.expectEqual(a.len, 1); | |
| 323 | ||
| 324 | const uninitialized = try a.addManyAsArray(4); | |
| 325 | try testing.expectEqual(uninitialized.len, 4); | |
| 326 | try testing.expectEqual(a.len, 5); | |
| 327 | ||
| 328 | try a.append(0xff); | |
| 329 | try testing.expectEqual(a.len, 6); | |
| 330 | try testing.expectEqual(a.pop(), 0xff); | |
| 331 | ||
| 332 | a.appendAssumeCapacity(0xff); | |
| 333 | try testing.expectEqual(a.len, 6); | |
| 334 | try testing.expectEqual(a.pop(), 0xff); | |
| 335 | ||
| 336 | try a.resize(1); | |
| 337 | try testing.expectEqual(a.pop(), 0); | |
| 338 | try testing.expectEqual(a.pop(), null); | |
| 339 | var unused = a.unusedCapacitySlice(); | |
| 340 | @memset(unused[0..8], 2); | |
| 341 | unused[8] = 3; | |
| 342 | unused[9] = 4; | |
| 343 | try testing.expectEqual(unused.len, a.capacity()); | |
| 344 | try a.resize(10); | |
| 345 | ||
| 346 | try a.insert(5, 0xaa); | |
| 347 | try testing.expectEqual(a.len, 11); | |
| 348 | try testing.expectEqual(a.get(5), 0xaa); | |
| 349 | try testing.expectEqual(a.get(9), 3); | |
| 350 | try testing.expectEqual(a.get(10), 4); | |
| 351 | ||
| 352 | try a.insert(11, 0xbb); | |
| 353 | try testing.expectEqual(a.len, 12); | |
| 354 | try testing.expectEqual(a.pop(), 0xbb); | |
| 355 | ||
| 356 | try a.appendSlice(&x); | |
| 357 | try testing.expectEqual(a.len, 11 + x.len); | |
| 358 | ||
| 359 | try a.appendNTimes(0xbb, 5); | |
| 360 | try testing.expectEqual(a.len, 11 + x.len + 5); | |
| 361 | try testing.expectEqual(a.pop(), 0xbb); | |
| 362 | ||
| 363 | a.appendNTimesAssumeCapacity(0xcc, 5); | |
| 364 | try testing.expectEqual(a.len, 11 + x.len + 5 - 1 + 5); | |
| 365 | try testing.expectEqual(a.pop(), 0xcc); | |
| 366 | ||
| 367 | try testing.expectEqual(a.len, 29); | |
| 368 | try a.replaceRange(1, 20, &x); | |
| 369 | try testing.expectEqual(a.len, 29 + x.len - 20); | |
| 370 | ||
| 371 | try a.insertSlice(0, &x); | |
| 372 | try testing.expectEqual(a.len, 29 + x.len - 20 + x.len); | |
| 373 | ||
| 374 | try a.replaceRange(1, 5, &x); | |
| 375 | try testing.expectEqual(a.len, 29 + x.len - 20 + x.len + x.len - 5); | |
| 376 | ||
| 377 | try a.append(10); | |
| 378 | try testing.expectEqual(a.pop(), 10); | |
| 379 | ||
| 380 | try a.append(20); | |
| 381 | const removed = a.orderedRemove(5); | |
| 382 | try testing.expectEqual(removed, 1); | |
| 383 | try testing.expectEqual(a.len, 34); | |
| 384 | ||
| 385 | a.set(0, 0xdd); | |
| 386 | a.set(a.len - 1, 0xee); | |
| 387 | const swapped = a.swapRemove(0); | |
| 388 | try testing.expectEqual(swapped, 0xdd); | |
| 389 | try testing.expectEqual(a.get(0), 0xee); | |
| 390 | ||
| 391 | const added_slice = try a.addManyAsSlice(3); | |
| 392 | try testing.expectEqual(added_slice.len, 3); | |
| 393 | try testing.expectEqual(a.len, 36); | |
| 394 | ||
| 395 | while (a.pop()) |_| {} | |
| 396 | const w = a.writer(); | |
| 397 | const s = "hello, this is a test string"; | |
| 398 | try w.writeAll(s); | |
| 399 | try testing.expectEqualStrings(s, a.constSlice()); | |
| 400 | } | |
| 401 | ||
| 402 | test "BoundedArrayAligned" { | |
| 403 | var a = try BoundedArrayAligned(u8, .@"16", 4).init(0); | |
| 404 | try a.append(0); | |
| 405 | try a.append(0); | |
| 406 | try a.append(255); | |
| 407 | try a.append(255); | |
| 408 | ||
| 409 | const b = @as(*const [2]u16, @ptrCast(a.constSlice().ptr)); | |
| 410 | try testing.expectEqual(@as(u16, 0), b[0]); | |
| 411 | try testing.expectEqual(@as(u16, 65535), b[1]); | |
| 412 | } |
lib/std/c.zig+6-6| ... | ... | @@ -6970,11 +6970,11 @@ pub const utsname = switch (native_os) { |
| 6970 | 6970 | domainname: [256:0]u8, |
| 6971 | 6971 | }, |
| 6972 | 6972 | .macos => extern struct { |
| 6973 | sysname: [256:0]u8, | |
| 6974 | nodename: [256:0]u8, | |
| 6975 | release: [256:0]u8, | |
| 6976 | version: [256:0]u8, | |
| 6977 | machine: [256:0]u8, | |
| 6973 | sysname: [255:0]u8, | |
| 6974 | nodename: [255:0]u8, | |
| 6975 | release: [255:0]u8, | |
| 6976 | version: [255:0]u8, | |
| 6977 | machine: [255:0]u8, | |
| 6978 | 6978 | }, |
| 6979 | 6979 | // https://github.com/SerenityOS/serenity/blob/d794ed1de7a46482272683f8dc4c858806390f29/Kernel/API/POSIX/sys/utsname.h#L17-L23 |
| 6980 | 6980 | .serenity => extern struct { |
| ... | ... | @@ -6984,7 +6984,7 @@ pub const utsname = switch (native_os) { |
| 6984 | 6984 | version: [UTSNAME_ENTRY_LEN:0]u8, |
| 6985 | 6985 | machine: [UTSNAME_ENTRY_LEN:0]u8, |
| 6986 | 6986 | |
| 6987 | const UTSNAME_ENTRY_LEN = 65; | |
| 6987 | const UTSNAME_ENTRY_LEN = 64; | |
| 6988 | 6988 | }, |
| 6989 | 6989 | else => void, |
| 6990 | 6990 | }; |
lib/std/compress/flate/Decompress.zig+4-3| ... | ... | @@ -373,7 +373,7 @@ fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader |
| 373 | 373 | d.state = .{ .stored_block = @intCast(remaining_len - n) }; |
| 374 | 374 | } |
| 375 | 375 | w.advance(n); |
| 376 | return n; | |
| 376 | return @intFromEnum(limit) - remaining + n; | |
| 377 | 377 | }, |
| 378 | 378 | .fixed_block => { |
| 379 | 379 | while (remaining > 0) { |
| ... | ... | @@ -603,7 +603,7 @@ fn tossBitsEnding(d: *Decompress, n: u4) !void { |
| 603 | 603 | error.EndOfStream => unreachable, |
| 604 | 604 | }; |
| 605 | 605 | d.next_bits = next_int >> needed_bits; |
| 606 | d.remaining_bits = @intCast(@as(usize, n) * 8 -| @as(usize, needed_bits)); | |
| 606 | d.remaining_bits = @intCast(@as(usize, buffered_n) * 8 -| @as(usize, needed_bits)); | |
| 607 | 607 | } |
| 608 | 608 | |
| 609 | 609 | fn takeBitsRuntime(d: *Decompress, n: u4) !u16 { |
| ... | ... | @@ -1265,6 +1265,7 @@ fn testDecompress(container: Container, compressed: []const u8, expected_plain: |
| 1265 | 1265 | defer aw.deinit(); |
| 1266 | 1266 | |
| 1267 | 1267 | var decompress: Decompress = .init(&in, container, &.{}); |
| 1268 | _ = try decompress.reader.streamRemaining(&aw.writer); | |
| 1268 | const decompressed_len = try decompress.reader.streamRemaining(&aw.writer); | |
| 1269 | try testing.expectEqual(expected_plain.len, decompressed_len); | |
| 1269 | 1270 | try testing.expectEqualSlices(u8, expected_plain, aw.getWritten()); |
| 1270 | 1271 | } |
lib/std/crypto/ecdsa.zig+1-1| ... | ... | @@ -58,7 +58,7 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { |
| 58 | 58 | pub const PublicKey = struct { |
| 59 | 59 | /// Length (in bytes) of a compressed sec1-encoded key. |
| 60 | 60 | pub const compressed_sec1_encoded_length = 1 + Curve.Fe.encoded_length; |
| 61 | /// Length (in bytes) of a compressed sec1-encoded key. | |
| 61 | /// Length (in bytes) of an uncompressed sec1-encoded key. | |
| 62 | 62 | pub const uncompressed_sec1_encoded_length = 1 + 2 * Curve.Fe.encoded_length; |
| 63 | 63 | |
| 64 | 64 | p: Curve, |
lib/std/crypto/tls/Client.zig+51-19| ... | ... | @@ -8,8 +8,8 @@ const mem = std.mem; |
| 8 | 8 | const crypto = std.crypto; |
| 9 | 9 | const assert = std.debug.assert; |
| 10 | 10 | const Certificate = std.crypto.Certificate; |
| 11 | const Reader = std.io.Reader; | |
| 12 | const Writer = std.io.Writer; | |
| 11 | const Reader = std.Io.Reader; | |
| 12 | const Writer = std.Io.Writer; | |
| 13 | 13 | |
| 14 | 14 | const max_ciphertext_len = tls.max_ciphertext_len; |
| 15 | 15 | const hmacExpandLabel = tls.hmacExpandLabel; |
| ... | ... | @@ -27,6 +27,8 @@ reader: Reader, |
| 27 | 27 | |
| 28 | 28 | /// The encrypted stream from the client to the server. Bytes are pushed here |
| 29 | 29 | /// via `writer`. |
| 30 | /// | |
| 31 | /// The buffer is asserted to have capacity at least `min_buffer_len`. | |
| 30 | 32 | output: *Writer, |
| 31 | 33 | /// The plaintext stream from the client to the server. |
| 32 | 34 | writer: Writer, |
| ... | ... | @@ -122,7 +124,6 @@ pub const Options = struct { |
| 122 | 124 | /// the amount of data expected, such as HTTP with the Content-Length header. |
| 123 | 125 | allow_truncation_attacks: bool = false, |
| 124 | 126 | write_buffer: []u8, |
| 125 | /// Asserted to have capacity at least `min_buffer_len`. | |
| 126 | 127 | read_buffer: []u8, |
| 127 | 128 | /// Populated when `error.TlsAlert` is returned from `init`. |
| 128 | 129 | alert: ?*tls.Alert = null, |
| ... | ... | @@ -185,6 +186,7 @@ const InitError = error{ |
| 185 | 186 | /// `input` is asserted to have buffer capacity at least `min_buffer_len`. |
| 186 | 187 | pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client { |
| 187 | 188 | assert(input.buffer.len >= min_buffer_len); |
| 189 | assert(output.buffer.len >= min_buffer_len); | |
| 188 | 190 | const host = switch (options.host) { |
| 189 | 191 | .no_verification => "", |
| 190 | 192 | .explicit => |host| host, |
| ... | ... | @@ -278,6 +280,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 278 | 280 | { |
| 279 | 281 | var iovecs: [2][]const u8 = .{ cleartext_header, host }; |
| 280 | 282 | try output.writeVecAll(iovecs[0..if (host.len == 0) 1 else 2]); |
| 283 | try output.flush(); | |
| 281 | 284 | } |
| 282 | 285 | |
| 283 | 286 | var tls_version: tls.ProtocolVersion = undefined; |
| ... | ... | @@ -328,7 +331,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 328 | 331 | var cleartext_bufs: [2][tls.max_ciphertext_inner_record_len]u8 = undefined; |
| 329 | 332 | fragment: while (true) { |
| 330 | 333 | // Ensure the input buffer pointer is stable in this scope. |
| 331 | input.rebaseCapacity(tls.max_ciphertext_record_len); | |
| 334 | input.rebase(tls.max_ciphertext_record_len) catch |err| switch (err) { | |
| 335 | error.EndOfStream => {}, // We have assurance the remainder of stream can be buffered. | |
| 336 | }; | |
| 332 | 337 | const record_header = input.peek(tls.record_header_len) catch |err| switch (err) { |
| 333 | 338 | error.EndOfStream => return error.TlsConnectionTruncated, |
| 334 | 339 | error.ReadFailed => return error.ReadFailed, |
| ... | ... | @@ -761,6 +766,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 761 | 766 | &client_verify_msg, |
| 762 | 767 | }; |
| 763 | 768 | try output.writeVecAll(&all_msgs_vec); |
| 769 | try output.flush(); | |
| 764 | 770 | }, |
| 765 | 771 | } |
| 766 | 772 | write_seq += 1; |
| ... | ... | @@ -826,6 +832,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 826 | 832 | &finished_msg, |
| 827 | 833 | }; |
| 828 | 834 | try output.writeVecAll(&all_msgs_vec); |
| 835 | try output.flush(); | |
| 829 | 836 | |
| 830 | 837 | const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length); |
| 831 | 838 | const server_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "s ap traffic", &handshake_hash, P.Hash.digest_length); |
| ... | ... | @@ -875,7 +882,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 875 | 882 | .buffer = options.write_buffer, |
| 876 | 883 | .vtable = &.{ |
| 877 | 884 | .drain = drain, |
| 878 | .sendFile = Writer.unimplementedSendFile, | |
| 885 | .flush = flush, | |
| 879 | 886 | }, |
| 880 | 887 | }, |
| 881 | 888 | .tls_version = tls_version, |
| ... | ... | @@ -908,32 +915,57 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 908 | 915 | } |
| 909 | 916 | |
| 910 | 917 | fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize { |
| 911 | const c: *Client = @fieldParentPtr("writer", w); | |
| 912 | if (true) @panic("update to use the buffer and flush"); | |
| 913 | const sliced_data = if (splat == 0) data[0..data.len -| 1] else data; | |
| 918 | const c: *Client = @alignCast(@fieldParentPtr("writer", w)); | |
| 914 | 919 | const output = c.output; |
| 915 | 920 | const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len); |
| 916 | var total_clear: usize = 0; | |
| 917 | 921 | var ciphertext_end: usize = 0; |
| 918 | for (sliced_data) |buf| { | |
| 919 | const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data); | |
| 920 | total_clear += prepared.cleartext_len; | |
| 921 | ciphertext_end += prepared.ciphertext_end; | |
| 922 | if (total_clear < buf.len) break; | |
| 922 | var total_clear: usize = 0; | |
| 923 | done: { | |
| 924 | { | |
| 925 | const buf = w.buffered(); | |
| 926 | const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data); | |
| 927 | total_clear += prepared.cleartext_len; | |
| 928 | ciphertext_end += prepared.ciphertext_end; | |
| 929 | if (prepared.cleartext_len < buf.len) break :done; | |
| 930 | } | |
| 931 | for (data[0 .. data.len - 1]) |buf| { | |
| 932 | if (buf.len < min_buffer_len) break :done; | |
| 933 | const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data); | |
| 934 | total_clear += prepared.cleartext_len; | |
| 935 | ciphertext_end += prepared.ciphertext_end; | |
| 936 | if (prepared.cleartext_len < buf.len) break :done; | |
| 937 | } | |
| 938 | const buf = data[data.len - 1]; | |
| 939 | for (0..splat) |_| { | |
| 940 | if (buf.len < min_buffer_len) break :done; | |
| 941 | const prepared = prepareCiphertextRecord(c, ciphertext_buf[ciphertext_end..], buf, .application_data); | |
| 942 | total_clear += prepared.cleartext_len; | |
| 943 | ciphertext_end += prepared.ciphertext_end; | |
| 944 | if (prepared.cleartext_len < buf.len) break :done; | |
| 945 | } | |
| 923 | 946 | } |
| 924 | 947 | output.advance(ciphertext_end); |
| 925 | return total_clear; | |
| 948 | return w.consume(total_clear); | |
| 949 | } | |
| 950 | ||
| 951 | fn flush(w: *Writer) Writer.Error!void { | |
| 952 | const c: *Client = @alignCast(@fieldParentPtr("writer", w)); | |
| 953 | const output = c.output; | |
| 954 | const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len); | |
| 955 | const prepared = prepareCiphertextRecord(c, ciphertext_buf, w.buffered(), .application_data); | |
| 956 | output.advance(prepared.ciphertext_end); | |
| 957 | w.end = 0; | |
| 926 | 958 | } |
| 927 | 959 | |
| 928 | 960 | /// Sends a `close_notify` alert, which is necessary for the server to |
| 929 | 961 | /// distinguish between a properly finished TLS session, or a truncation |
| 930 | 962 | /// attack. |
| 931 | 963 | pub fn end(c: *Client) Writer.Error!void { |
| 964 | try flush(&c.writer); | |
| 932 | 965 | const output = c.output; |
| 933 | 966 | const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len); |
| 934 | 967 | const prepared = prepareCiphertextRecord(c, ciphertext_buf, &tls.close_notify_alert, .alert); |
| 935 | output.advance(prepared.cleartext_len); | |
| 936 | return prepared.ciphertext_end; | |
| 968 | output.advance(prepared.ciphertext_end); | |
| 937 | 969 | } |
| 938 | 970 | |
| 939 | 971 | fn prepareCiphertextRecord( |
| ... | ... | @@ -1043,8 +1075,8 @@ pub fn eof(c: Client) bool { |
| 1043 | 1075 | return c.received_close_notify; |
| 1044 | 1076 | } |
| 1045 | 1077 | |
| 1046 | fn stream(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize { | |
| 1047 | const c: *Client = @fieldParentPtr("reader", r); | |
| 1078 | fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize { | |
| 1079 | const c: *Client = @alignCast(@fieldParentPtr("reader", r)); | |
| 1048 | 1080 | if (c.eof()) return error.EndOfStream; |
| 1049 | 1081 | const input = c.input; |
| 1050 | 1082 | // If at least one full encrypted record is not buffered, read once. |
lib/std/elf.zig+92-38| ... | ... | @@ -502,6 +502,13 @@ pub const Header = struct { |
| 502 | 502 | }; |
| 503 | 503 | } |
| 504 | 504 | |
| 505 | pub fn iterateProgramHeadersBuffer(h: Header, buf: []const u8) ProgramHeaderBufferIterator { | |
| 506 | return .{ | |
| 507 | .elf_header = h, | |
| 508 | .buf = buf, | |
| 509 | }; | |
| 510 | } | |
| 511 | ||
| 505 | 512 | pub fn iterateSectionHeaders(h: Header, file_reader: *std.fs.File.Reader) SectionHeaderIterator { |
| 506 | 513 | return .{ |
| 507 | 514 | .elf_header = h, |
| ... | ... | @@ -509,6 +516,13 @@ pub const Header = struct { |
| 509 | 516 | }; |
| 510 | 517 | } |
| 511 | 518 | |
| 519 | pub fn iterateSectionHeadersBuffer(h: Header, buf: []const u8) SectionHeaderBufferIterator { | |
| 520 | return .{ | |
| 521 | .elf_header = h, | |
| 522 | .buf = buf, | |
| 523 | }; | |
| 524 | } | |
| 525 | ||
| 512 | 526 | pub const ReadError = std.Io.Reader.Error || error{ |
| 513 | 527 | InvalidElfMagic, |
| 514 | 528 | InvalidElfVersion, |
| ... | ... | @@ -570,29 +584,48 @@ pub const ProgramHeaderIterator = struct { |
| 570 | 584 | if (it.index >= it.elf_header.phnum) return null; |
| 571 | 585 | defer it.index += 1; |
| 572 | 586 | |
| 573 | if (it.elf_header.is_64) { | |
| 574 | const offset = it.elf_header.phoff + @sizeOf(Elf64_Phdr) * it.index; | |
| 575 | try it.file_reader.seekTo(offset); | |
| 576 | const phdr = try it.file_reader.interface.takeStruct(Elf64_Phdr, it.elf_header.endian); | |
| 577 | return phdr; | |
| 578 | } | |
| 579 | ||
| 580 | const offset = it.elf_header.phoff + @sizeOf(Elf32_Phdr) * it.index; | |
| 587 | const offset = it.elf_header.phoff + if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr) * it.index; | |
| 581 | 588 | try it.file_reader.seekTo(offset); |
| 582 | const phdr = try it.file_reader.interface.takeStruct(Elf32_Phdr, it.elf_header.endian); | |
| 583 | return .{ | |
| 584 | .p_type = phdr.p_type, | |
| 585 | .p_offset = phdr.p_offset, | |
| 586 | .p_vaddr = phdr.p_vaddr, | |
| 587 | .p_paddr = phdr.p_paddr, | |
| 588 | .p_filesz = phdr.p_filesz, | |
| 589 | .p_memsz = phdr.p_memsz, | |
| 590 | .p_flags = phdr.p_flags, | |
| 591 | .p_align = phdr.p_align, | |
| 592 | }; | |
| 589 | ||
| 590 | return takePhdr(&it.file_reader.interface, it.elf_header); | |
| 591 | } | |
| 592 | }; | |
| 593 | ||
| 594 | pub const ProgramHeaderBufferIterator = struct { | |
| 595 | elf_header: Header, | |
| 596 | buf: []const u8, | |
| 597 | index: usize = 0, | |
| 598 | ||
| 599 | pub fn next(it: *ProgramHeaderBufferIterator) !?Elf64_Phdr { | |
| 600 | if (it.index >= it.elf_header.phnum) return null; | |
| 601 | defer it.index += 1; | |
| 602 | ||
| 603 | const offset = it.elf_header.phoff + if (it.elf_header.is_64) @sizeOf(Elf64_Phdr) else @sizeOf(Elf32_Phdr) * it.index; | |
| 604 | var reader = std.Io.Reader.fixed(it.buf[offset..]); | |
| 605 | ||
| 606 | return takePhdr(&reader, it.elf_header); | |
| 593 | 607 | } |
| 594 | 608 | }; |
| 595 | 609 | |
| 610 | fn takePhdr(reader: *std.io.Reader, elf_header: Header) !?Elf64_Phdr { | |
| 611 | if (elf_header.is_64) { | |
| 612 | const phdr = try reader.takeStruct(Elf64_Phdr, elf_header.endian); | |
| 613 | return phdr; | |
| 614 | } | |
| 615 | ||
| 616 | const phdr = try reader.takeStruct(Elf32_Phdr, elf_header.endian); | |
| 617 | return .{ | |
| 618 | .p_type = phdr.p_type, | |
| 619 | .p_offset = phdr.p_offset, | |
| 620 | .p_vaddr = phdr.p_vaddr, | |
| 621 | .p_paddr = phdr.p_paddr, | |
| 622 | .p_filesz = phdr.p_filesz, | |
| 623 | .p_memsz = phdr.p_memsz, | |
| 624 | .p_flags = phdr.p_flags, | |
| 625 | .p_align = phdr.p_align, | |
| 626 | }; | |
| 627 | } | |
| 628 | ||
| 596 | 629 | pub const SectionHeaderIterator = struct { |
| 597 | 630 | elf_header: Header, |
| 598 | 631 | file_reader: *std.fs.File.Reader, |
| ... | ... | @@ -602,29 +635,50 @@ pub const SectionHeaderIterator = struct { |
| 602 | 635 | if (it.index >= it.elf_header.shnum) return null; |
| 603 | 636 | defer it.index += 1; |
| 604 | 637 | |
| 605 | if (it.elf_header.is_64) { | |
| 606 | try it.file_reader.seekTo(it.elf_header.shoff + @sizeOf(Elf64_Shdr) * it.index); | |
| 607 | const shdr = try it.file_reader.interface.takeStruct(Elf64_Shdr, it.elf_header.endian); | |
| 608 | return shdr; | |
| 609 | } | |
| 638 | const offset = it.elf_header.shoff + if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr) * it.index; | |
| 639 | try it.file_reader.seekTo(offset); | |
| 610 | 640 | |
| 611 | try it.file_reader.seekTo(it.elf_header.shoff + @sizeOf(Elf32_Shdr) * it.index); | |
| 612 | const shdr = try it.file_reader.interface.takeStruct(Elf32_Shdr, it.elf_header.endian); | |
| 613 | return .{ | |
| 614 | .sh_name = shdr.sh_name, | |
| 615 | .sh_type = shdr.sh_type, | |
| 616 | .sh_flags = shdr.sh_flags, | |
| 617 | .sh_addr = shdr.sh_addr, | |
| 618 | .sh_offset = shdr.sh_offset, | |
| 619 | .sh_size = shdr.sh_size, | |
| 620 | .sh_link = shdr.sh_link, | |
| 621 | .sh_info = shdr.sh_info, | |
| 622 | .sh_addralign = shdr.sh_addralign, | |
| 623 | .sh_entsize = shdr.sh_entsize, | |
| 624 | }; | |
| 641 | return takeShdr(&it.file_reader.interface, it.elf_header); | |
| 642 | } | |
| 643 | }; | |
| 644 | ||
| 645 | pub const SectionHeaderBufferIterator = struct { | |
| 646 | elf_header: Header, | |
| 647 | buf: []const u8, | |
| 648 | index: usize = 0, | |
| 649 | ||
| 650 | pub fn next(it: *SectionHeaderBufferIterator) !?Elf64_Shdr { | |
| 651 | if (it.index >= it.elf_header.shnum) return null; | |
| 652 | defer it.index += 1; | |
| 653 | ||
| 654 | const offset = it.elf_header.shoff + if (it.elf_header.is_64) @sizeOf(Elf64_Shdr) else @sizeOf(Elf32_Shdr) * it.index; | |
| 655 | var reader = std.Io.Reader.fixed(it.buf[offset..]); | |
| 656 | ||
| 657 | return takeShdr(&reader, it.elf_header); | |
| 625 | 658 | } |
| 626 | 659 | }; |
| 627 | 660 | |
| 661 | fn takeShdr(reader: *std.Io.Reader, elf_header: Header) !?Elf64_Shdr { | |
| 662 | if (elf_header.is_64) { | |
| 663 | const shdr = try reader.takeStruct(Elf64_Shdr, elf_header.endian); | |
| 664 | return shdr; | |
| 665 | } | |
| 666 | ||
| 667 | const shdr = try reader.takeStruct(Elf32_Shdr, elf_header.endian); | |
| 668 | return .{ | |
| 669 | .sh_name = shdr.sh_name, | |
| 670 | .sh_type = shdr.sh_type, | |
| 671 | .sh_flags = shdr.sh_flags, | |
| 672 | .sh_addr = shdr.sh_addr, | |
| 673 | .sh_offset = shdr.sh_offset, | |
| 674 | .sh_size = shdr.sh_size, | |
| 675 | .sh_link = shdr.sh_link, | |
| 676 | .sh_info = shdr.sh_info, | |
| 677 | .sh_addralign = shdr.sh_addralign, | |
| 678 | .sh_entsize = shdr.sh_entsize, | |
| 679 | }; | |
| 680 | } | |
| 681 | ||
| 628 | 682 | pub const ELFCLASSNONE = 0; |
| 629 | 683 | pub const ELFCLASS32 = 1; |
| 630 | 684 | pub const ELFCLASS64 = 2; |
lib/std/fs/File.zig+22-6| ... | ... | @@ -1111,7 +1111,16 @@ pub const Reader = struct { |
| 1111 | 1111 | if (is_windows) { |
| 1112 | 1112 | // Unfortunately, `ReadFileScatter` cannot be used since it |
| 1113 | 1113 | // requires page alignment. |
| 1114 | return readPositional(r, data[0]); | |
| 1114 | assert(io_reader.seek == io_reader.end); | |
| 1115 | io_reader.seek = 0; | |
| 1116 | io_reader.end = 0; | |
| 1117 | const first = data[0]; | |
| 1118 | if (first.len >= io_reader.buffer.len) { | |
| 1119 | return readPositional(r, first); | |
| 1120 | } else { | |
| 1121 | io_reader.end += try readPositional(r, io_reader.buffer); | |
| 1122 | return 0; | |
| 1123 | } | |
| 1115 | 1124 | } |
| 1116 | 1125 | var iovecs_buffer: [max_buffers_len]posix.iovec = undefined; |
| 1117 | 1126 | const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data); |
| ... | ... | @@ -1141,8 +1150,7 @@ pub const Reader = struct { |
| 1141 | 1150 | } |
| 1142 | 1151 | r.pos += n; |
| 1143 | 1152 | if (n > data_size) { |
| 1144 | io_reader.seek = 0; | |
| 1145 | io_reader.end = n - data_size; | |
| 1153 | io_reader.end += n - data_size; | |
| 1146 | 1154 | return data_size; |
| 1147 | 1155 | } |
| 1148 | 1156 | return n; |
| ... | ... | @@ -1151,7 +1159,16 @@ pub const Reader = struct { |
| 1151 | 1159 | if (is_windows) { |
| 1152 | 1160 | // Unfortunately, `ReadFileScatter` cannot be used since it |
| 1153 | 1161 | // requires page alignment. |
| 1154 | return readStreaming(r, data[0]); | |
| 1162 | assert(io_reader.seek == io_reader.end); | |
| 1163 | io_reader.seek = 0; | |
| 1164 | io_reader.end = 0; | |
| 1165 | const first = data[0]; | |
| 1166 | if (first.len >= io_reader.buffer.len) { | |
| 1167 | return readStreaming(r, first); | |
| 1168 | } else { | |
| 1169 | io_reader.end += try readStreaming(r, io_reader.buffer); | |
| 1170 | return 0; | |
| 1171 | } | |
| 1155 | 1172 | } |
| 1156 | 1173 | var iovecs_buffer: [max_buffers_len]posix.iovec = undefined; |
| 1157 | 1174 | const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data); |
| ... | ... | @@ -1167,8 +1184,7 @@ pub const Reader = struct { |
| 1167 | 1184 | } |
| 1168 | 1185 | r.pos += n; |
| 1169 | 1186 | if (n > data_size) { |
| 1170 | io_reader.seek = 0; | |
| 1171 | io_reader.end = n - data_size; | |
| 1187 | io_reader.end += n - data_size; | |
| 1172 | 1188 | return data_size; |
| 1173 | 1189 | } |
| 1174 | 1190 | return n; |
lib/std/http.zig+125-112| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std.zig"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | const Writer = std.io.Writer; | |
| 4 | const Writer = std.Io.Writer; | |
| 5 | 5 | const File = std.fs.File; |
| 6 | 6 | |
| 7 | 7 | pub const Client = @import("http/Client.zig"); |
| ... | ... | @@ -20,51 +20,32 @@ pub const Version = enum { |
| 20 | 20 | /// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definition |
| 21 | 21 | /// |
| 22 | 22 | /// https://datatracker.ietf.org/doc/html/rfc5789#section-2 PATCH |
| 23 | pub const Method = enum(u64) { | |
| 24 | GET = parse("GET"), | |
| 25 | HEAD = parse("HEAD"), | |
| 26 | POST = parse("POST"), | |
| 27 | PUT = parse("PUT"), | |
| 28 | DELETE = parse("DELETE"), | |
| 29 | CONNECT = parse("CONNECT"), | |
| 30 | OPTIONS = parse("OPTIONS"), | |
| 31 | TRACE = parse("TRACE"), | |
| 32 | PATCH = parse("PATCH"), | |
| 33 | ||
| 34 | _, | |
| 35 | ||
| 36 | /// Converts `s` into a type that may be used as a `Method` field. | |
| 37 | /// Asserts that `s` is 24 or fewer bytes. | |
| 38 | pub fn parse(s: []const u8) u64 { | |
| 39 | var x: u64 = 0; | |
| 40 | const len = @min(s.len, @sizeOf(@TypeOf(x))); | |
| 41 | @memcpy(std.mem.asBytes(&x)[0..len], s[0..len]); | |
| 42 | return x; | |
| 43 | } | |
| 44 | ||
| 45 | pub fn format(self: Method, w: *std.io.Writer) std.io.Writer.Error!void { | |
| 46 | const bytes: []const u8 = @ptrCast(&@intFromEnum(self)); | |
| 47 | const str = std.mem.sliceTo(bytes, 0); | |
| 48 | try w.writeAll(str); | |
| 49 | } | |
| 23 | pub const Method = enum { | |
| 24 | GET, | |
| 25 | HEAD, | |
| 26 | POST, | |
| 27 | PUT, | |
| 28 | DELETE, | |
| 29 | CONNECT, | |
| 30 | OPTIONS, | |
| 31 | TRACE, | |
| 32 | PATCH, | |
| 50 | 33 | |
| 51 | 34 | /// Returns true if a request of this method is allowed to have a body |
| 52 | 35 | /// Actual behavior from servers may vary and should still be checked |
| 53 | pub fn requestHasBody(self: Method) bool { | |
| 54 | return switch (self) { | |
| 36 | pub fn requestHasBody(m: Method) bool { | |
| 37 | return switch (m) { | |
| 55 | 38 | .POST, .PUT, .PATCH => true, |
| 56 | 39 | .GET, .HEAD, .DELETE, .CONNECT, .OPTIONS, .TRACE => false, |
| 57 | else => true, | |
| 58 | 40 | }; |
| 59 | 41 | } |
| 60 | 42 | |
| 61 | 43 | /// Returns true if a response to this method is allowed to have a body |
| 62 | 44 | /// Actual behavior from clients may vary and should still be checked |
| 63 | pub fn responseHasBody(self: Method) bool { | |
| 64 | return switch (self) { | |
| 45 | pub fn responseHasBody(m: Method) bool { | |
| 46 | return switch (m) { | |
| 65 | 47 | .GET, .POST, .DELETE, .CONNECT, .OPTIONS, .PATCH => true, |
| 66 | 48 | .HEAD, .PUT, .TRACE => false, |
| 67 | else => true, | |
| 68 | 49 | }; |
| 69 | 50 | } |
| 70 | 51 | |
| ... | ... | @@ -73,11 +54,10 @@ pub const Method = enum(u64) { |
| 73 | 54 | /// https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP |
| 74 | 55 | /// |
| 75 | 56 | /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1 |
| 76 | pub fn safe(self: Method) bool { | |
| 77 | return switch (self) { | |
| 57 | pub fn safe(m: Method) bool { | |
| 58 | return switch (m) { | |
| 78 | 59 | .GET, .HEAD, .OPTIONS, .TRACE => true, |
| 79 | 60 | .POST, .PUT, .DELETE, .CONNECT, .PATCH => false, |
| 80 | else => false, | |
| 81 | 61 | }; |
| 82 | 62 | } |
| 83 | 63 | |
| ... | ... | @@ -88,11 +68,10 @@ pub const Method = enum(u64) { |
| 88 | 68 | /// https://developer.mozilla.org/en-US/docs/Glossary/Idempotent |
| 89 | 69 | /// |
| 90 | 70 | /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.2 |
| 91 | pub fn idempotent(self: Method) bool { | |
| 92 | return switch (self) { | |
| 71 | pub fn idempotent(m: Method) bool { | |
| 72 | return switch (m) { | |
| 93 | 73 | .GET, .HEAD, .PUT, .DELETE, .OPTIONS, .TRACE => true, |
| 94 | 74 | .CONNECT, .POST, .PATCH => false, |
| 95 | else => false, | |
| 96 | 75 | }; |
| 97 | 76 | } |
| 98 | 77 | |
| ... | ... | @@ -102,11 +81,10 @@ pub const Method = enum(u64) { |
| 102 | 81 | /// https://developer.mozilla.org/en-US/docs/Glossary/cacheable |
| 103 | 82 | /// |
| 104 | 83 | /// https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.3 |
| 105 | pub fn cacheable(self: Method) bool { | |
| 106 | return switch (self) { | |
| 84 | pub fn cacheable(m: Method) bool { | |
| 85 | return switch (m) { | |
| 107 | 86 | .GET, .HEAD => true, |
| 108 | 87 | .POST, .PUT, .DELETE, .CONNECT, .OPTIONS, .TRACE, .PATCH => false, |
| 109 | else => false, | |
| 110 | 88 | }; |
| 111 | 89 | } |
| 112 | 90 | }; |
| ... | ... | @@ -327,11 +305,11 @@ pub const Header = struct { |
| 327 | 305 | }; |
| 328 | 306 | |
| 329 | 307 | pub const Reader = struct { |
| 330 | in: *std.io.Reader, | |
| 308 | in: *std.Io.Reader, | |
| 331 | 309 | /// This is preallocated memory that might be used by `bodyReader`. That |
| 332 | 310 | /// function might return a pointer to this field, or a different |
| 333 | /// `*std.io.Reader`. Advisable to not access this field directly. | |
| 334 | interface: std.io.Reader, | |
| 311 | /// `*std.Io.Reader`. Advisable to not access this field directly. | |
| 312 | interface: std.Io.Reader, | |
| 335 | 313 | /// Keeps track of whether the stream is ready to accept a new request, |
| 336 | 314 | /// making invalid API usage cause assertion failures rather than HTTP |
| 337 | 315 | /// protocol violations. |
| ... | ... | @@ -343,10 +321,6 @@ pub const Reader = struct { |
| 343 | 321 | /// read from `in`. |
| 344 | 322 | trailers: []const u8 = &.{}, |
| 345 | 323 | body_err: ?BodyError = null, |
| 346 | /// Stolen from `in`. | |
| 347 | head_buffer: []u8 = &.{}, | |
| 348 | ||
| 349 | pub const max_chunk_header_len = 22; | |
| 350 | 324 | |
| 351 | 325 | pub const RemainingChunkLen = enum(u64) { |
| 352 | 326 | head = 0, |
| ... | ... | @@ -398,35 +372,34 @@ pub const Reader = struct { |
| 398 | 372 | ReadFailed, |
| 399 | 373 | }; |
| 400 | 374 | |
| 401 | pub fn restituteHeadBuffer(reader: *Reader) void { | |
| 402 | reader.in.restitute(reader.head_buffer.len); | |
| 403 | reader.head_buffer.len = 0; | |
| 404 | } | |
| 405 | ||
| 406 | /// Buffers the entire head into `head_buffer`, invalidating the previous | |
| 407 | /// `head_buffer`, if any. | |
| 408 | pub fn receiveHead(reader: *Reader) HeadError!void { | |
| 375 | /// Buffers the entire head inside `in`. | |
| 376 | /// | |
| 377 | /// The resulting memory is invalidated by any subsequent consumption of | |
| 378 | /// the input stream. | |
| 379 | pub fn receiveHead(reader: *Reader) HeadError![]const u8 { | |
| 409 | 380 | reader.trailers = &.{}; |
| 410 | 381 | const in = reader.in; |
| 411 | in.restitute(reader.head_buffer.len); | |
| 412 | reader.head_buffer.len = 0; | |
| 413 | in.rebase(); | |
| 414 | 382 | var hp: HeadParser = .{}; |
| 415 | var head_end: usize = 0; | |
| 383 | var head_len: usize = 0; | |
| 416 | 384 | while (true) { |
| 417 | if (head_end >= in.buffer.len) return error.HttpHeadersOversize; | |
| 418 | in.fillMore() catch |err| switch (err) { | |
| 419 | error.EndOfStream => switch (head_end) { | |
| 420 | 0 => return error.HttpConnectionClosing, | |
| 421 | else => return error.HttpRequestTruncated, | |
| 422 | }, | |
| 423 | error.ReadFailed => return error.ReadFailed, | |
| 424 | }; | |
| 425 | head_end += hp.feed(in.buffered()[head_end..]); | |
| 385 | if (in.buffer.len - head_len == 0) return error.HttpHeadersOversize; | |
| 386 | const remaining = in.buffered()[head_len..]; | |
| 387 | if (remaining.len == 0) { | |
| 388 | in.fillMore() catch |err| switch (err) { | |
| 389 | error.EndOfStream => switch (head_len) { | |
| 390 | 0 => return error.HttpConnectionClosing, | |
| 391 | else => return error.HttpRequestTruncated, | |
| 392 | }, | |
| 393 | error.ReadFailed => return error.ReadFailed, | |
| 394 | }; | |
| 395 | continue; | |
| 396 | } | |
| 397 | head_len += hp.feed(remaining); | |
| 426 | 398 | if (hp.state == .finished) { |
| 427 | reader.head_buffer = in.steal(head_end); | |
| 428 | 399 | reader.state = .received_head; |
| 429 | return; | |
| 400 | const head_buffer = in.buffered()[0..head_len]; | |
| 401 | in.toss(head_len); | |
| 402 | return head_buffer; | |
| 430 | 403 | } |
| 431 | 404 | } |
| 432 | 405 | } |
| ... | ... | @@ -442,7 +415,7 @@ pub const Reader = struct { |
| 442 | 415 | buffer: []u8, |
| 443 | 416 | transfer_encoding: TransferEncoding, |
| 444 | 417 | content_length: ?u64, |
| 445 | ) *std.io.Reader { | |
| 418 | ) *std.Io.Reader { | |
| 446 | 419 | assert(reader.state == .received_head); |
| 447 | 420 | switch (transfer_encoding) { |
| 448 | 421 | .chunked => { |
| ... | ... | @@ -492,7 +465,7 @@ pub const Reader = struct { |
| 492 | 465 | content_encoding: ContentEncoding, |
| 493 | 466 | decompressor: *Decompressor, |
| 494 | 467 | decompression_buffer: []u8, |
| 495 | ) *std.io.Reader { | |
| 468 | ) *std.Io.Reader { | |
| 496 | 469 | if (transfer_encoding == .none and content_length == null) { |
| 497 | 470 | assert(reader.state == .received_head); |
| 498 | 471 | reader.state = .body_none; |
| ... | ... | @@ -501,7 +474,7 @@ pub const Reader = struct { |
| 501 | 474 | return reader.in; |
| 502 | 475 | }, |
| 503 | 476 | .deflate => { |
| 504 | decompressor.* = .{ .flate = .init(reader.in, .raw, decompression_buffer) }; | |
| 477 | decompressor.* = .{ .flate = .init(reader.in, .zlib, decompression_buffer) }; | |
| 505 | 478 | return &decompressor.flate.reader; |
| 506 | 479 | }, |
| 507 | 480 | .gzip => { |
| ... | ... | @@ -520,37 +493,37 @@ pub const Reader = struct { |
| 520 | 493 | } |
| 521 | 494 | |
| 522 | 495 | fn contentLengthStream( |
| 523 | io_r: *std.io.Reader, | |
| 496 | io_r: *std.Io.Reader, | |
| 524 | 497 | w: *Writer, |
| 525 | limit: std.io.Limit, | |
| 526 | ) std.io.Reader.StreamError!usize { | |
| 527 | const reader: *Reader = @fieldParentPtr("interface", io_r); | |
| 498 | limit: std.Io.Limit, | |
| 499 | ) std.Io.Reader.StreamError!usize { | |
| 500 | const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); | |
| 528 | 501 | const remaining_content_length = &reader.state.body_remaining_content_length; |
| 529 | 502 | const remaining = remaining_content_length.*; |
| 530 | 503 | if (remaining == 0) { |
| 531 | 504 | reader.state = .ready; |
| 532 | 505 | return error.EndOfStream; |
| 533 | 506 | } |
| 534 | const n = try reader.in.stream(w, limit.min(.limited(remaining))); | |
| 507 | const n = try reader.in.stream(w, limit.min(.limited64(remaining))); | |
| 535 | 508 | remaining_content_length.* = remaining - n; |
| 536 | 509 | return n; |
| 537 | 510 | } |
| 538 | 511 | |
| 539 | fn contentLengthDiscard(io_r: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize { | |
| 540 | const reader: *Reader = @fieldParentPtr("interface", io_r); | |
| 512 | fn contentLengthDiscard(io_r: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize { | |
| 513 | const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); | |
| 541 | 514 | const remaining_content_length = &reader.state.body_remaining_content_length; |
| 542 | 515 | const remaining = remaining_content_length.*; |
| 543 | 516 | if (remaining == 0) { |
| 544 | 517 | reader.state = .ready; |
| 545 | 518 | return error.EndOfStream; |
| 546 | 519 | } |
| 547 | const n = try reader.in.discard(limit.min(.limited(remaining))); | |
| 520 | const n = try reader.in.discard(limit.min(.limited64(remaining))); | |
| 548 | 521 | remaining_content_length.* = remaining - n; |
| 549 | 522 | return n; |
| 550 | 523 | } |
| 551 | 524 | |
| 552 | fn chunkedStream(io_r: *std.io.Reader, w: *Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize { | |
| 553 | const reader: *Reader = @fieldParentPtr("interface", io_r); | |
| 525 | fn chunkedStream(io_r: *std.Io.Reader, w: *Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { | |
| 526 | const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); | |
| 554 | 527 | const chunk_len_ptr = switch (reader.state) { |
| 555 | 528 | .ready => return error.EndOfStream, |
| 556 | 529 | .body_remaining_chunk_len => |*x| x, |
| ... | ... | @@ -573,9 +546,9 @@ pub const Reader = struct { |
| 573 | 546 | fn chunkedReadEndless( |
| 574 | 547 | reader: *Reader, |
| 575 | 548 | w: *Writer, |
| 576 | limit: std.io.Limit, | |
| 549 | limit: std.Io.Limit, | |
| 577 | 550 | chunk_len_ptr: *RemainingChunkLen, |
| 578 | ) (BodyError || std.io.Reader.StreamError)!usize { | |
| 551 | ) (BodyError || std.Io.Reader.StreamError)!usize { | |
| 579 | 552 | const in = reader.in; |
| 580 | 553 | len: switch (chunk_len_ptr.*) { |
| 581 | 554 | .head => { |
| ... | ... | @@ -596,7 +569,7 @@ pub const Reader = struct { |
| 596 | 569 | } |
| 597 | 570 | } |
| 598 | 571 | if (cp.chunk_len == 0) return parseTrailers(reader, 0); |
| 599 | const n = try in.stream(w, limit.min(.limited(cp.chunk_len))); | |
| 572 | const n = try in.stream(w, limit.min(.limited64(cp.chunk_len))); | |
| 600 | 573 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| 601 | 574 | return n; |
| 602 | 575 | }, |
| ... | ... | @@ -612,15 +585,15 @@ pub const Reader = struct { |
| 612 | 585 | continue :len .head; |
| 613 | 586 | }, |
| 614 | 587 | else => |remaining_chunk_len| { |
| 615 | const n = try in.stream(w, limit.min(.limited(@intFromEnum(remaining_chunk_len) - 2))); | |
| 588 | const n = try in.stream(w, limit.min(.limited64(@intFromEnum(remaining_chunk_len) - 2))); | |
| 616 | 589 | chunk_len_ptr.* = .init(@intFromEnum(remaining_chunk_len) - n); |
| 617 | 590 | return n; |
| 618 | 591 | }, |
| 619 | 592 | } |
| 620 | 593 | } |
| 621 | 594 | |
| 622 | fn chunkedDiscard(io_r: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize { | |
| 623 | const reader: *Reader = @fieldParentPtr("interface", io_r); | |
| 595 | fn chunkedDiscard(io_r: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize { | |
| 596 | const reader: *Reader = @alignCast(@fieldParentPtr("interface", io_r)); | |
| 624 | 597 | const chunk_len_ptr = switch (reader.state) { |
| 625 | 598 | .ready => return error.EndOfStream, |
| 626 | 599 | .body_remaining_chunk_len => |*x| x, |
| ... | ... | @@ -641,9 +614,9 @@ pub const Reader = struct { |
| 641 | 614 | |
| 642 | 615 | fn chunkedDiscardEndless( |
| 643 | 616 | reader: *Reader, |
| 644 | limit: std.io.Limit, | |
| 617 | limit: std.Io.Limit, | |
| 645 | 618 | chunk_len_ptr: *RemainingChunkLen, |
| 646 | ) (BodyError || std.io.Reader.Error)!usize { | |
| 619 | ) (BodyError || std.Io.Reader.Error)!usize { | |
| 647 | 620 | const in = reader.in; |
| 648 | 621 | len: switch (chunk_len_ptr.*) { |
| 649 | 622 | .head => { |
| ... | ... | @@ -664,7 +637,7 @@ pub const Reader = struct { |
| 664 | 637 | } |
| 665 | 638 | } |
| 666 | 639 | if (cp.chunk_len == 0) return parseTrailers(reader, 0); |
| 667 | const n = try in.discard(limit.min(.limited(cp.chunk_len))); | |
| 640 | const n = try in.discard(limit.min(.limited64(cp.chunk_len))); | |
| 668 | 641 | chunk_len_ptr.* = .init(cp.chunk_len + 2 - n); |
| 669 | 642 | return n; |
| 670 | 643 | }, |
| ... | ... | @@ -680,7 +653,7 @@ pub const Reader = struct { |
| 680 | 653 | continue :len .head; |
| 681 | 654 | }, |
| 682 | 655 | else => |remaining_chunk_len| { |
| 683 | const n = try in.discard(limit.min(.limited(remaining_chunk_len.int() - 2))); | |
| 656 | const n = try in.discard(limit.min(.limited64(remaining_chunk_len.int() - 2))); | |
| 684 | 657 | chunk_len_ptr.* = .init(remaining_chunk_len.int() - n); |
| 685 | 658 | return n; |
| 686 | 659 | }, |
| ... | ... | @@ -689,7 +662,7 @@ pub const Reader = struct { |
| 689 | 662 | |
| 690 | 663 | /// Called when next bytes in the stream are trailers, or "\r\n" to indicate |
| 691 | 664 | /// end of chunked body. |
| 692 | fn parseTrailers(reader: *Reader, amt_read: usize) (BodyError || std.io.Reader.Error)!usize { | |
| 665 | fn parseTrailers(reader: *Reader, amt_read: usize) (BodyError || std.Io.Reader.Error)!usize { | |
| 693 | 666 | const in = reader.in; |
| 694 | 667 | const rn = try in.peekArray(2); |
| 695 | 668 | if (rn[0] == '\r' and rn[1] == '\n') { |
| ... | ... | @@ -721,21 +694,21 @@ pub const Reader = struct { |
| 721 | 694 | pub const Decompressor = union(enum) { |
| 722 | 695 | flate: std.compress.flate.Decompress, |
| 723 | 696 | zstd: std.compress.zstd.Decompress, |
| 724 | none: *std.io.Reader, | |
| 697 | none: *std.Io.Reader, | |
| 725 | 698 | |
| 726 | 699 | pub fn init( |
| 727 | 700 | decompressor: *Decompressor, |
| 728 | transfer_reader: *std.io.Reader, | |
| 701 | transfer_reader: *std.Io.Reader, | |
| 729 | 702 | buffer: []u8, |
| 730 | 703 | content_encoding: ContentEncoding, |
| 731 | ) *std.io.Reader { | |
| 704 | ) *std.Io.Reader { | |
| 732 | 705 | switch (content_encoding) { |
| 733 | 706 | .identity => { |
| 734 | 707 | decompressor.* = .{ .none = transfer_reader }; |
| 735 | 708 | return transfer_reader; |
| 736 | 709 | }, |
| 737 | 710 | .deflate => { |
| 738 | decompressor.* = .{ .flate = .init(transfer_reader, .raw, buffer) }; | |
| 711 | decompressor.* = .{ .flate = .init(transfer_reader, .zlib, buffer) }; | |
| 739 | 712 | return &decompressor.flate.reader; |
| 740 | 713 | }, |
| 741 | 714 | .gzip => { |
| ... | ... | @@ -763,7 +736,7 @@ pub const BodyWriter = struct { |
| 763 | 736 | |
| 764 | 737 | /// How many zeroes to reserve for hex-encoded chunk length. |
| 765 | 738 | const chunk_len_digits = 8; |
| 766 | const max_chunk_len: usize = std.math.pow(usize, 16, chunk_len_digits) - 1; | |
| 739 | const max_chunk_len: usize = std.math.pow(u64, 16, chunk_len_digits) - 1; | |
| 767 | 740 | const chunk_header_template = ("0" ** chunk_len_digits) ++ "\r\n"; |
| 768 | 741 | |
| 769 | 742 | comptime { |
| ... | ... | @@ -795,7 +768,7 @@ pub const BodyWriter = struct { |
| 795 | 768 | }; |
| 796 | 769 | |
| 797 | 770 | pub fn isEliding(w: *const BodyWriter) bool { |
| 798 | return w.writer.vtable.drain == Writer.discardingDrain; | |
| 771 | return w.writer.vtable.drain == elidingDrain; | |
| 799 | 772 | } |
| 800 | 773 | |
| 801 | 774 | /// Sends all buffered data across `BodyWriter.http_protocol_output`. |
| ... | ... | @@ -923,7 +896,7 @@ pub const BodyWriter = struct { |
| 923 | 896 | } |
| 924 | 897 | |
| 925 | 898 | pub fn contentLengthDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { |
| 926 | const bw: *BodyWriter = @fieldParentPtr("writer", w); | |
| 899 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 927 | 900 | assert(!bw.isEliding()); |
| 928 | 901 | const out = bw.http_protocol_output; |
| 929 | 902 | const n = try out.writeSplatHeader(w.buffered(), data, splat); |
| ... | ... | @@ -932,24 +905,64 @@ pub const BodyWriter = struct { |
| 932 | 905 | } |
| 933 | 906 | |
| 934 | 907 | pub fn noneDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { |
| 935 | const bw: *BodyWriter = @fieldParentPtr("writer", w); | |
| 908 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 936 | 909 | assert(!bw.isEliding()); |
| 937 | 910 | const out = bw.http_protocol_output; |
| 938 | 911 | const n = try out.writeSplatHeader(w.buffered(), data, splat); |
| 939 | 912 | return w.consume(n); |
| 940 | 913 | } |
| 941 | 914 | |
| 915 | pub fn elidingDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { | |
| 916 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 917 | const slice = data[0 .. data.len - 1]; | |
| 918 | const pattern = data[slice.len]; | |
| 919 | var written: usize = pattern.len * splat; | |
| 920 | for (slice) |bytes| written += bytes.len; | |
| 921 | switch (bw.state) { | |
| 922 | .content_length => |*len| len.* -= written + w.end, | |
| 923 | else => {}, | |
| 924 | } | |
| 925 | w.end = 0; | |
| 926 | return written; | |
| 927 | } | |
| 928 | ||
| 929 | pub fn elidingSendFile(w: *Writer, file_reader: *File.Reader, limit: std.Io.Limit) Writer.FileError!usize { | |
| 930 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 931 | if (File.Handle == void) return error.Unimplemented; | |
| 932 | if (builtin.zig_backend == .stage2_aarch64) return error.Unimplemented; | |
| 933 | switch (bw.state) { | |
| 934 | .content_length => |*len| len.* -= w.end, | |
| 935 | else => {}, | |
| 936 | } | |
| 937 | w.end = 0; | |
| 938 | if (limit == .nothing) return 0; | |
| 939 | if (file_reader.getSize()) |size| { | |
| 940 | const n = limit.minInt64(size - file_reader.pos); | |
| 941 | if (n == 0) return error.EndOfStream; | |
| 942 | file_reader.seekBy(@intCast(n)) catch return error.Unimplemented; | |
| 943 | switch (bw.state) { | |
| 944 | .content_length => |*len| len.* -= n, | |
| 945 | else => {}, | |
| 946 | } | |
| 947 | return n; | |
| 948 | } else |_| { | |
| 949 | // Error is observable on `file_reader` instance, and it is better to | |
| 950 | // treat the file as a pipe. | |
| 951 | return error.Unimplemented; | |
| 952 | } | |
| 953 | } | |
| 954 | ||
| 942 | 955 | /// Returns `null` if size cannot be computed without making any syscalls. |
| 943 | pub fn noneSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize { | |
| 944 | const bw: *BodyWriter = @fieldParentPtr("writer", w); | |
| 956 | pub fn noneSendFile(w: *Writer, file_reader: *File.Reader, limit: std.Io.Limit) Writer.FileError!usize { | |
| 957 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 945 | 958 | assert(!bw.isEliding()); |
| 946 | 959 | const out = bw.http_protocol_output; |
| 947 | 960 | const n = try out.sendFileHeader(w.buffered(), file_reader, limit); |
| 948 | 961 | return w.consume(n); |
| 949 | 962 | } |
| 950 | 963 | |
| 951 | pub fn contentLengthSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize { | |
| 952 | const bw: *BodyWriter = @fieldParentPtr("writer", w); | |
| 964 | pub fn contentLengthSendFile(w: *Writer, file_reader: *File.Reader, limit: std.Io.Limit) Writer.FileError!usize { | |
| 965 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 953 | 966 | assert(!bw.isEliding()); |
| 954 | 967 | const out = bw.http_protocol_output; |
| 955 | 968 | const n = try out.sendFileHeader(w.buffered(), file_reader, limit); |
| ... | ... | @@ -957,8 +970,8 @@ pub const BodyWriter = struct { |
| 957 | 970 | return w.consume(n); |
| 958 | 971 | } |
| 959 | 972 | |
| 960 | pub fn chunkedSendFile(w: *Writer, file_reader: *File.Reader, limit: std.io.Limit) Writer.FileError!usize { | |
| 961 | const bw: *BodyWriter = @fieldParentPtr("writer", w); | |
| 973 | pub fn chunkedSendFile(w: *Writer, file_reader: *File.Reader, limit: std.Io.Limit) Writer.FileError!usize { | |
| 974 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 962 | 975 | assert(!bw.isEliding()); |
| 963 | 976 | const data_len = Writer.countSendFileLowerBound(w.end, file_reader, limit) orelse { |
| 964 | 977 | // If the file size is unknown, we cannot lower to a `sendFile` since we would |
| ... | ... | @@ -1006,7 +1019,7 @@ pub const BodyWriter = struct { |
| 1006 | 1019 | } |
| 1007 | 1020 | |
| 1008 | 1021 | pub fn chunkedDrain(w: *Writer, data: []const []const u8, splat: usize) Error!usize { |
| 1009 | const bw: *BodyWriter = @fieldParentPtr("writer", w); | |
| 1022 | const bw: *BodyWriter = @alignCast(@fieldParentPtr("writer", w)); | |
| 1010 | 1023 | assert(!bw.isEliding()); |
| 1011 | 1024 | const out = bw.http_protocol_output; |
| 1012 | 1025 | const data_len = w.end + Writer.countSplat(data, splat); |
lib/std/http/Client.zig+86-44| ... | ... | @@ -42,7 +42,7 @@ connection_pool: ConnectionPool = .{}, |
| 42 | 42 | /// |
| 43 | 43 | /// If the entire HTTP header cannot fit in this amount of bytes, |
| 44 | 44 | /// `error.HttpHeadersOversize` will be returned from `Request.wait`. |
| 45 | read_buffer_size: usize = 4096, | |
| 45 | read_buffer_size: usize = 4096 + if (disable_tls) 0 else std.crypto.tls.Client.min_buffer_len, | |
| 46 | 46 | /// Each `Connection` allocates this amount for the writer buffer. |
| 47 | 47 | write_buffer_size: usize = 1024, |
| 48 | 48 | |
| ... | ... | @@ -82,7 +82,7 @@ pub const ConnectionPool = struct { |
| 82 | 82 | |
| 83 | 83 | var next = pool.free.last; |
| 84 | 84 | while (next) |node| : (next = node.prev) { |
| 85 | const connection: *Connection = @fieldParentPtr("pool_node", node); | |
| 85 | const connection: *Connection = @alignCast(@fieldParentPtr("pool_node", node)); | |
| 86 | 86 | if (connection.protocol != criteria.protocol) continue; |
| 87 | 87 | if (connection.port != criteria.port) continue; |
| 88 | 88 | |
| ... | ... | @@ -115,8 +115,6 @@ pub const ConnectionPool = struct { |
| 115 | 115 | /// Tries to release a connection back to the connection pool. |
| 116 | 116 | /// If the connection is marked as closing, it will be closed instead. |
| 117 | 117 | /// |
| 118 | /// `allocator` must be the same one used to create `connection`. | |
| 119 | /// | |
| 120 | 118 | /// Threadsafe. |
| 121 | 119 | pub fn release(pool: *ConnectionPool, connection: *Connection) void { |
| 122 | 120 | pool.mutex.lock(); |
| ... | ... | @@ -127,7 +125,7 @@ pub const ConnectionPool = struct { |
| 127 | 125 | if (connection.closing or pool.free_size == 0) return connection.destroy(); |
| 128 | 126 | |
| 129 | 127 | if (pool.free_len >= pool.free_size) { |
| 130 | const popped: *Connection = @fieldParentPtr("pool_node", pool.free.popFirst().?); | |
| 128 | const popped: *Connection = @alignCast(@fieldParentPtr("pool_node", pool.free.popFirst().?)); | |
| 131 | 129 | pool.free_len -= 1; |
| 132 | 130 | |
| 133 | 131 | popped.destroy(); |
| ... | ... | @@ -183,14 +181,14 @@ pub const ConnectionPool = struct { |
| 183 | 181 | |
| 184 | 182 | var next = pool.free.first; |
| 185 | 183 | while (next) |node| { |
| 186 | const connection: *Connection = @fieldParentPtr("pool_node", node); | |
| 184 | const connection: *Connection = @alignCast(@fieldParentPtr("pool_node", node)); | |
| 187 | 185 | next = node.next; |
| 188 | 186 | connection.destroy(); |
| 189 | 187 | } |
| 190 | 188 | |
| 191 | 189 | next = pool.used.first; |
| 192 | 190 | while (next) |node| { |
| 193 | const connection: *Connection = @fieldParentPtr("pool_node", node); | |
| 191 | const connection: *Connection = @alignCast(@fieldParentPtr("pool_node", node)); | |
| 194 | 192 | next = node.next; |
| 195 | 193 | connection.destroy(); |
| 196 | 194 | } |
| ... | ... | @@ -306,15 +304,16 @@ pub const Connection = struct { |
| 306 | 304 | const host_buffer = base[@sizeOf(Tls)..][0..remote_host.len]; |
| 307 | 305 | const tls_read_buffer = host_buffer.ptr[host_buffer.len..][0..client.tls_buffer_size]; |
| 308 | 306 | const tls_write_buffer = tls_read_buffer.ptr[tls_read_buffer.len..][0..client.tls_buffer_size]; |
| 309 | const socket_write_buffer = tls_write_buffer.ptr[tls_write_buffer.len..][0..client.write_buffer_size]; | |
| 310 | assert(base.ptr + alloc_len == socket_write_buffer.ptr + socket_write_buffer.len); | |
| 307 | const write_buffer = tls_write_buffer.ptr[tls_write_buffer.len..][0..client.write_buffer_size]; | |
| 308 | const read_buffer = write_buffer.ptr[write_buffer.len..][0..client.read_buffer_size]; | |
| 309 | assert(base.ptr + alloc_len == read_buffer.ptr + read_buffer.len); | |
| 311 | 310 | @memcpy(host_buffer, remote_host); |
| 312 | 311 | const tls: *Tls = @ptrCast(base); |
| 313 | 312 | tls.* = .{ |
| 314 | 313 | .connection = .{ |
| 315 | 314 | .client = client, |
| 316 | .stream_writer = stream.writer(socket_write_buffer), | |
| 317 | .stream_reader = stream.reader(&.{}), | |
| 315 | .stream_writer = stream.writer(tls_write_buffer), | |
| 316 | .stream_reader = stream.reader(tls_read_buffer), | |
| 318 | 317 | .pool_node = .{}, |
| 319 | 318 | .port = port, |
| 320 | 319 | .host_len = @intCast(remote_host.len), |
| ... | ... | @@ -330,8 +329,8 @@ pub const Connection = struct { |
| 330 | 329 | .host = .{ .explicit = remote_host }, |
| 331 | 330 | .ca = .{ .bundle = client.ca_bundle }, |
| 332 | 331 | .ssl_key_log = client.ssl_key_log, |
| 333 | .read_buffer = tls_read_buffer, | |
| 334 | .write_buffer = tls_write_buffer, | |
| 332 | .read_buffer = read_buffer, | |
| 333 | .write_buffer = write_buffer, | |
| 335 | 334 | // This is appropriate for HTTPS because the HTTP headers contain |
| 336 | 335 | // the content length which is used to detect truncation attacks. |
| 337 | 336 | .allow_truncation_attacks = true, |
| ... | ... | @@ -349,7 +348,8 @@ pub const Connection = struct { |
| 349 | 348 | } |
| 350 | 349 | |
| 351 | 350 | fn allocLen(client: *Client, host_len: usize) usize { |
| 352 | return @sizeOf(Tls) + host_len + client.tls_buffer_size + client.tls_buffer_size + client.write_buffer_size; | |
| 351 | return @sizeOf(Tls) + host_len + client.tls_buffer_size + client.tls_buffer_size + | |
| 352 | client.write_buffer_size + client.read_buffer_size; | |
| 353 | 353 | } |
| 354 | 354 | |
| 355 | 355 | fn host(tls: *Tls) []u8 { |
| ... | ... | @@ -358,6 +358,21 @@ pub const Connection = struct { |
| 358 | 358 | } |
| 359 | 359 | }; |
| 360 | 360 | |
| 361 | pub const ReadError = std.crypto.tls.Client.ReadError || std.net.Stream.ReadError; | |
| 362 | ||
| 363 | pub fn getReadError(c: *const Connection) ?ReadError { | |
| 364 | return switch (c.protocol) { | |
| 365 | .tls => { | |
| 366 | if (disable_tls) unreachable; | |
| 367 | const tls: *const Tls = @alignCast(@fieldParentPtr("connection", c)); | |
| 368 | return tls.client.read_err orelse c.stream_reader.getError(); | |
| 369 | }, | |
| 370 | .plain => { | |
| 371 | return c.stream_reader.getError(); | |
| 372 | }, | |
| 373 | }; | |
| 374 | } | |
| 375 | ||
| 361 | 376 | fn getStream(c: *Connection) net.Stream { |
| 362 | 377 | return c.stream_reader.getStream(); |
| 363 | 378 | } |
| ... | ... | @@ -366,11 +381,11 @@ pub const Connection = struct { |
| 366 | 381 | return switch (c.protocol) { |
| 367 | 382 | .tls => { |
| 368 | 383 | if (disable_tls) unreachable; |
| 369 | const tls: *Tls = @fieldParentPtr("connection", c); | |
| 384 | const tls: *Tls = @alignCast(@fieldParentPtr("connection", c)); | |
| 370 | 385 | return tls.host(); |
| 371 | 386 | }, |
| 372 | 387 | .plain => { |
| 373 | const plain: *Plain = @fieldParentPtr("connection", c); | |
| 388 | const plain: *Plain = @alignCast(@fieldParentPtr("connection", c)); | |
| 374 | 389 | return plain.host(); |
| 375 | 390 | }, |
| 376 | 391 | }; |
| ... | ... | @@ -383,11 +398,11 @@ pub const Connection = struct { |
| 383 | 398 | switch (c.protocol) { |
| 384 | 399 | .tls => { |
| 385 | 400 | if (disable_tls) unreachable; |
| 386 | const tls: *Tls = @fieldParentPtr("connection", c); | |
| 401 | const tls: *Tls = @alignCast(@fieldParentPtr("connection", c)); | |
| 387 | 402 | tls.destroy(); |
| 388 | 403 | }, |
| 389 | 404 | .plain => { |
| 390 | const plain: *Plain = @fieldParentPtr("connection", c); | |
| 405 | const plain: *Plain = @alignCast(@fieldParentPtr("connection", c)); | |
| 391 | 406 | plain.destroy(); |
| 392 | 407 | }, |
| 393 | 408 | } |
| ... | ... | @@ -399,7 +414,7 @@ pub const Connection = struct { |
| 399 | 414 | return switch (c.protocol) { |
| 400 | 415 | .tls => { |
| 401 | 416 | if (disable_tls) unreachable; |
| 402 | const tls: *Tls = @fieldParentPtr("connection", c); | |
| 417 | const tls: *Tls = @alignCast(@fieldParentPtr("connection", c)); | |
| 403 | 418 | return &tls.client.writer; |
| 404 | 419 | }, |
| 405 | 420 | .plain => &c.stream_writer.interface, |
| ... | ... | @@ -412,7 +427,7 @@ pub const Connection = struct { |
| 412 | 427 | return switch (c.protocol) { |
| 413 | 428 | .tls => { |
| 414 | 429 | if (disable_tls) unreachable; |
| 415 | const tls: *Tls = @fieldParentPtr("connection", c); | |
| 430 | const tls: *Tls = @alignCast(@fieldParentPtr("connection", c)); | |
| 416 | 431 | return &tls.client.reader; |
| 417 | 432 | }, |
| 418 | 433 | .plain => c.stream_reader.interface(), |
| ... | ... | @@ -422,7 +437,7 @@ pub const Connection = struct { |
| 422 | 437 | pub fn flush(c: *Connection) Writer.Error!void { |
| 423 | 438 | if (c.protocol == .tls) { |
| 424 | 439 | if (disable_tls) unreachable; |
| 425 | const tls: *Tls = @fieldParentPtr("connection", c); | |
| 440 | const tls: *Tls = @alignCast(@fieldParentPtr("connection", c)); | |
| 426 | 441 | try tls.client.writer.flush(); |
| 427 | 442 | } |
| 428 | 443 | try c.stream_writer.interface.flush(); |
| ... | ... | @@ -434,9 +449,8 @@ pub const Connection = struct { |
| 434 | 449 | pub fn end(c: *Connection) Writer.Error!void { |
| 435 | 450 | if (c.protocol == .tls) { |
| 436 | 451 | if (disable_tls) unreachable; |
| 437 | const tls: *Tls = @fieldParentPtr("connection", c); | |
| 452 | const tls: *Tls = @alignCast(@fieldParentPtr("connection", c)); | |
| 438 | 453 | try tls.client.end(); |
| 439 | try tls.client.writer.flush(); | |
| 440 | 454 | } |
| 441 | 455 | try c.stream_writer.interface.flush(); |
| 442 | 456 | } |
| ... | ... | @@ -444,8 +458,8 @@ pub const Connection = struct { |
| 444 | 458 | |
| 445 | 459 | pub const Response = struct { |
| 446 | 460 | request: *Request, |
| 447 | /// Pointers in this struct are invalidated with the next call to | |
| 448 | /// `receiveHead`. | |
| 461 | /// Pointers in this struct are invalidated when the response body stream | |
| 462 | /// is initialized. | |
| 449 | 463 | head: Head, |
| 450 | 464 | |
| 451 | 465 | pub const Head = struct { |
| ... | ... | @@ -484,10 +498,8 @@ pub const Response = struct { |
| 484 | 498 | }; |
| 485 | 499 | var it = mem.splitSequence(u8, bytes, "\r\n"); |
| 486 | 500 | |
| 487 | const first_line = it.next().?; | |
| 488 | if (first_line.len < 12) { | |
| 489 | return error.HttpHeadersInvalid; | |
| 490 | } | |
| 501 | const first_line = it.first(); | |
| 502 | if (first_line.len < 12) return error.HttpHeadersInvalid; | |
| 491 | 503 | |
| 492 | 504 | const version: http.Version = switch (int64(first_line[0..8])) { |
| 493 | 505 | int64("HTTP/1.0") => .@"HTTP/1.0", |
| ... | ... | @@ -671,6 +683,16 @@ pub const Response = struct { |
| 671 | 683 | try expectEqual(@as(u10, 418), parseInt3("418")); |
| 672 | 684 | try expectEqual(@as(u10, 999), parseInt3("999")); |
| 673 | 685 | } |
| 686 | ||
| 687 | /// Help the programmer avoid bugs by calling this when the string | |
| 688 | /// memory of `Head` becomes invalidated. | |
| 689 | fn invalidateStrings(h: *Head) void { | |
| 690 | h.bytes = undefined; | |
| 691 | h.reason = undefined; | |
| 692 | if (h.location) |*s| s.* = undefined; | |
| 693 | if (h.content_type) |*s| s.* = undefined; | |
| 694 | if (h.content_disposition) |*s| s.* = undefined; | |
| 695 | } | |
| 674 | 696 | }; |
| 675 | 697 | |
| 676 | 698 | /// If compressed body has been negotiated this will return compressed bytes. |
| ... | ... | @@ -683,6 +705,7 @@ pub const Response = struct { |
| 683 | 705 | /// See also: |
| 684 | 706 | /// * `readerDecompressing` |
| 685 | 707 | pub fn reader(response: *Response, buffer: []u8) *Reader { |
| 708 | response.head.invalidateStrings(); | |
| 686 | 709 | const req = response.request; |
| 687 | 710 | if (!req.method.responseHasBody()) return .ending; |
| 688 | 711 | const head = &response.head; |
| ... | ... | @@ -703,6 +726,7 @@ pub const Response = struct { |
| 703 | 726 | decompressor: *http.Decompressor, |
| 704 | 727 | decompression_buffer: []u8, |
| 705 | 728 | ) *Reader { |
| 729 | response.head.invalidateStrings(); | |
| 706 | 730 | const head = &response.head; |
| 707 | 731 | return response.request.reader.bodyReaderDecompressing( |
| 708 | 732 | head.transfer_encoding, |
| ... | ... | @@ -805,6 +829,11 @@ pub const Request = struct { |
| 805 | 829 | unhandled = std.math.maxInt(u16), |
| 806 | 830 | _, |
| 807 | 831 | |
| 832 | pub fn init(n: u16) RedirectBehavior { | |
| 833 | assert(n != std.math.maxInt(u16)); | |
| 834 | return @enumFromInt(n); | |
| 835 | } | |
| 836 | ||
| 808 | 837 | pub fn subtractOne(rb: *RedirectBehavior) void { |
| 809 | 838 | switch (rb.*) { |
| 810 | 839 | .not_allowed => unreachable, |
| ... | ... | @@ -821,7 +850,6 @@ pub const Request = struct { |
| 821 | 850 | |
| 822 | 851 | /// Returns the request's `Connection` back to the pool of the `Client`. |
| 823 | 852 | pub fn deinit(r: *Request) void { |
| 824 | r.reader.restituteHeadBuffer(); | |
| 825 | 853 | if (r.connection) |connection| { |
| 826 | 854 | connection.closing = connection.closing or switch (r.reader.state) { |
| 827 | 855 | .ready => false, |
| ... | ... | @@ -856,6 +884,15 @@ pub const Request = struct { |
| 856 | 884 | return result; |
| 857 | 885 | } |
| 858 | 886 | |
| 887 | /// Transfers the HTTP head and body over the connection and flushes. | |
| 888 | pub fn sendBodyComplete(r: *Request, body: []u8) Writer.Error!void { | |
| 889 | r.transfer_encoding = .{ .content_length = body.len }; | |
| 890 | var bw = try sendBodyUnflushed(r, body); | |
| 891 | bw.writer.end = body.len; | |
| 892 | try bw.end(); | |
| 893 | try r.connection.?.flush(); | |
| 894 | } | |
| 895 | ||
| 859 | 896 | /// Transfers the HTTP head over the connection, which is not flushed until |
| 860 | 897 | /// `BodyWriter.flush` or `BodyWriter.end` is called. |
| 861 | 898 | /// |
| ... | ... | @@ -908,13 +945,13 @@ pub const Request = struct { |
| 908 | 945 | const connection = r.connection.?; |
| 909 | 946 | const w = connection.writer(); |
| 910 | 947 | |
| 911 | try r.method.write(w); | |
| 948 | try w.writeAll(@tagName(r.method)); | |
| 912 | 949 | try w.writeByte(' '); |
| 913 | 950 | |
| 914 | 951 | if (r.method == .CONNECT) { |
| 915 | try uri.writeToStream(.{ .authority = true }, w); | |
| 952 | try uri.writeToStream(w, .{ .authority = true }); | |
| 916 | 953 | } else { |
| 917 | try uri.writeToStream(.{ | |
| 954 | try uri.writeToStream(w, .{ | |
| 918 | 955 | .scheme = connection.proxied, |
| 919 | 956 | .authentication = connection.proxied, |
| 920 | 957 | .authority = connection.proxied, |
| ... | ... | @@ -928,7 +965,7 @@ pub const Request = struct { |
| 928 | 965 | |
| 929 | 966 | if (try emitOverridableHeader("host: ", r.headers.host, w)) { |
| 930 | 967 | try w.writeAll("host: "); |
| 931 | try uri.writeToStream(.{ .authority = true }, w); | |
| 968 | try uri.writeToStream(w, .{ .authority = true }); | |
| 932 | 969 | try w.writeAll("\r\n"); |
| 933 | 970 | } |
| 934 | 971 | |
| ... | ... | @@ -1043,13 +1080,16 @@ pub const Request = struct { |
| 1043 | 1080 | /// buffer capacity would be exceeded, `error.HttpRedirectLocationOversize` |
| 1044 | 1081 | /// is returned instead. This buffer may be empty if no redirects are to be |
| 1045 | 1082 | /// handled. |
| 1083 | /// | |
| 1084 | /// If this fails with `error.ReadFailed` then the `Connection.getReadError` | |
| 1085 | /// method of `r.connection` can be used to get more detailed information. | |
| 1046 | 1086 | pub fn receiveHead(r: *Request, redirect_buffer: []u8) ReceiveHeadError!Response { |
| 1047 | 1087 | var aux_buf = redirect_buffer; |
| 1048 | 1088 | while (true) { |
| 1049 | try r.reader.receiveHead(); | |
| 1089 | const head_buffer = try r.reader.receiveHead(); | |
| 1050 | 1090 | const response: Response = .{ |
| 1051 | 1091 | .request = r, |
| 1052 | .head = Response.Head.parse(r.reader.head_buffer) catch return error.HttpHeadersInvalid, | |
| 1092 | .head = Response.Head.parse(head_buffer) catch return error.HttpHeadersInvalid, | |
| 1053 | 1093 | }; |
| 1054 | 1094 | const head = &response.head; |
| 1055 | 1095 | |
| ... | ... | @@ -1121,7 +1161,6 @@ pub const Request = struct { |
| 1121 | 1161 | _ = reader.discardRemaining() catch |err| switch (err) { |
| 1122 | 1162 | error.ReadFailed => return r.reader.body_err.?, |
| 1123 | 1163 | }; |
| 1124 | r.reader.restituteHeadBuffer(); | |
| 1125 | 1164 | } |
| 1126 | 1165 | const new_uri = r.uri.resolveInPlace(location.len, aux_buf) catch |err| switch (err) { |
| 1127 | 1166 | error.UnexpectedCharacter => return error.HttpRedirectLocationInvalid, |
| ... | ... | @@ -1298,16 +1337,17 @@ pub const basic_authorization = struct { |
| 1298 | 1337 | pub fn value(uri: Uri, out: []u8) []u8 { |
| 1299 | 1338 | var bw: Writer = .fixed(out); |
| 1300 | 1339 | write(uri, &bw) catch unreachable; |
| 1301 | return bw.getWritten(); | |
| 1340 | return bw.buffered(); | |
| 1302 | 1341 | } |
| 1303 | 1342 | |
| 1304 | 1343 | pub fn write(uri: Uri, out: *Writer) Writer.Error!void { |
| 1305 | var buf: [max_user_len + ":".len + max_password_len]u8 = undefined; | |
| 1344 | var buf: [max_user_len + 1 + max_password_len]u8 = undefined; | |
| 1306 | 1345 | var w: Writer = .fixed(&buf); |
| 1307 | w.print("{fuser}:{fpassword}", .{ | |
| 1308 | uri.user orelse Uri.Component.empty, | |
| 1309 | uri.password orelse Uri.Component.empty, | |
| 1310 | }) catch unreachable; | |
| 1346 | const user: Uri.Component = uri.user orelse .empty; | |
| 1347 | const password: Uri.Component = uri.user orelse .empty; | |
| 1348 | user.formatUser(&w) catch unreachable; | |
| 1349 | w.writeByte(':') catch unreachable; | |
| 1350 | password.formatPassword(&w) catch unreachable; | |
| 1311 | 1351 | try out.print("Basic {b64}", .{w.buffered()}); |
| 1312 | 1352 | } |
| 1313 | 1353 | }; |
| ... | ... | @@ -1697,6 +1737,7 @@ pub const FetchError = Uri.ParseError || RequestError || Request.ReceiveHeadErro |
| 1697 | 1737 | StreamTooLong, |
| 1698 | 1738 | /// TODO provide optional diagnostics when this occurs or break into more error codes |
| 1699 | 1739 | WriteFailed, |
| 1740 | UnsupportedCompressionMethod, | |
| 1700 | 1741 | }; |
| 1701 | 1742 | |
| 1702 | 1743 | /// Perform a one-shot HTTP request with the provided options. |
| ... | ... | @@ -1748,7 +1789,8 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult { |
| 1748 | 1789 | const decompress_buffer: []u8 = switch (response.head.content_encoding) { |
| 1749 | 1790 | .identity => &.{}, |
| 1750 | 1791 | .zstd => options.decompress_buffer orelse try client.allocator.alloc(u8, std.compress.zstd.default_window_len), |
| 1751 | else => options.decompress_buffer orelse try client.allocator.alloc(u8, 8 * 1024), | |
| 1792 | .deflate, .gzip => options.decompress_buffer orelse try client.allocator.alloc(u8, std.compress.flate.max_window_len), | |
| 1793 | .compress => return error.UnsupportedCompressionMethod, | |
| 1752 | 1794 | }; |
| 1753 | 1795 | defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer); |
| 1754 | 1796 |
lib/std/http/Server.zig+64-47| ... | ... | @@ -6,7 +6,8 @@ const mem = std.mem; |
| 6 | 6 | const Uri = std.Uri; |
| 7 | 7 | const assert = std.debug.assert; |
| 8 | 8 | const testing = std.testing; |
| 9 | const Writer = std.io.Writer; | |
| 9 | const Writer = std.Io.Writer; | |
| 10 | const Reader = std.Io.Reader; | |
| 10 | 11 | |
| 11 | 12 | const Server = @This(); |
| 12 | 13 | |
| ... | ... | @@ -21,7 +22,7 @@ reader: http.Reader, |
| 21 | 22 | /// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`. |
| 22 | 23 | /// |
| 23 | 24 | /// The returned `Server` is ready for `receiveHead` to be called. |
| 24 | pub fn init(in: *std.io.Reader, out: *Writer) Server { | |
| 25 | pub fn init(in: *Reader, out: *Writer) Server { | |
| 25 | 26 | return .{ |
| 26 | 27 | .reader = .{ |
| 27 | 28 | .in = in, |
| ... | ... | @@ -33,33 +34,31 @@ pub fn init(in: *std.io.Reader, out: *Writer) Server { |
| 33 | 34 | }; |
| 34 | 35 | } |
| 35 | 36 | |
| 36 | pub fn deinit(s: *Server) void { | |
| 37 | s.reader.restituteHeadBuffer(); | |
| 38 | } | |
| 39 | ||
| 40 | 37 | pub const ReceiveHeadError = http.Reader.HeadError || error{ |
| 41 | 38 | /// Client sent headers that did not conform to the HTTP protocol. |
| 42 | 39 | /// |
| 43 | /// To find out more detailed diagnostics, `http.Reader.head_buffer` can be | |
| 40 | /// To find out more detailed diagnostics, `Request.head_buffer` can be | |
| 44 | 41 | /// passed directly to `Request.Head.parse`. |
| 45 | 42 | HttpHeadersInvalid, |
| 46 | 43 | }; |
| 47 | 44 | |
| 48 | 45 | pub fn receiveHead(s: *Server) ReceiveHeadError!Request { |
| 49 | try s.reader.receiveHead(); | |
| 46 | const head_buffer = try s.reader.receiveHead(); | |
| 50 | 47 | return .{ |
| 51 | 48 | .server = s, |
| 49 | .head_buffer = head_buffer, | |
| 52 | 50 | // No need to track the returned error here since users can repeat the |
| 53 | 51 | // parse with the header buffer to get detailed diagnostics. |
| 54 | .head = Request.Head.parse(s.reader.head_buffer) catch return error.HttpHeadersInvalid, | |
| 52 | .head = Request.Head.parse(head_buffer) catch return error.HttpHeadersInvalid, | |
| 55 | 53 | }; |
| 56 | 54 | } |
| 57 | 55 | |
| 58 | 56 | pub const Request = struct { |
| 59 | 57 | server: *Server, |
| 60 | /// Pointers in this struct are invalidated with the next call to | |
| 61 | /// `receiveHead`. | |
| 58 | /// Pointers in this struct are invalidated when the request body stream is | |
| 59 | /// initialized. | |
| 62 | 60 | head: Head, |
| 61 | head_buffer: []const u8, | |
| 63 | 62 | respond_err: ?RespondError = null, |
| 64 | 63 | |
| 65 | 64 | pub const RespondError = error{ |
| ... | ... | @@ -98,10 +97,9 @@ pub const Request = struct { |
| 98 | 97 | |
| 99 | 98 | const method_end = mem.indexOfScalar(u8, first_line, ' ') orelse |
| 100 | 99 | return error.HttpHeadersInvalid; |
| 101 | if (method_end > 24) return error.HttpHeadersInvalid; | |
| 102 | 100 | |
| 103 | const method_str = first_line[0..method_end]; | |
| 104 | const method: http.Method = @enumFromInt(http.Method.parse(method_str)); | |
| 101 | const method = std.meta.stringToEnum(http.Method, first_line[0..method_end]) orelse | |
| 102 | return error.UnknownHttpMethod; | |
| 105 | 103 | |
| 106 | 104 | const version_start = mem.lastIndexOfScalar(u8, first_line, ' ') orelse |
| 107 | 105 | return error.HttpHeadersInvalid; |
| ... | ... | @@ -225,11 +223,19 @@ pub const Request = struct { |
| 225 | 223 | inline fn int64(array: *const [8]u8) u64 { |
| 226 | 224 | return @bitCast(array.*); |
| 227 | 225 | } |
| 226 | ||
| 227 | /// Help the programmer avoid bugs by calling this when the string | |
| 228 | /// memory of `Head` becomes invalidated. | |
| 229 | fn invalidateStrings(h: *Head) void { | |
| 230 | h.target = undefined; | |
| 231 | if (h.expect) |*s| s.* = undefined; | |
| 232 | if (h.content_type) |*s| s.* = undefined; | |
| 233 | } | |
| 228 | 234 | }; |
| 229 | 235 | |
| 230 | pub fn iterateHeaders(r: *Request) http.HeaderIterator { | |
| 236 | pub fn iterateHeaders(r: *const Request) http.HeaderIterator { | |
| 231 | 237 | assert(r.server.reader.state == .received_head); |
| 232 | return http.HeaderIterator.init(r.server.reader.head_buffer); | |
| 238 | return http.HeaderIterator.init(r.head_buffer); | |
| 233 | 239 | } |
| 234 | 240 | |
| 235 | 241 | test iterateHeaders { |
| ... | ... | @@ -244,7 +250,6 @@ pub const Request = struct { |
| 244 | 250 | .reader = .{ |
| 245 | 251 | .in = undefined, |
| 246 | 252 | .state = .received_head, |
| 247 | .head_buffer = @constCast(request_bytes), | |
| 248 | 253 | .interface = undefined, |
| 249 | 254 | }, |
| 250 | 255 | .out = undefined, |
| ... | ... | @@ -253,6 +258,7 @@ pub const Request = struct { |
| 253 | 258 | var request: Request = .{ |
| 254 | 259 | .server = &server, |
| 255 | 260 | .head = undefined, |
| 261 | .head_buffer = @constCast(request_bytes), | |
| 256 | 262 | }; |
| 257 | 263 | |
| 258 | 264 | var it = request.iterateHeaders(); |
| ... | ... | @@ -435,10 +441,8 @@ pub const Request = struct { |
| 435 | 441 | |
| 436 | 442 | for (o.extra_headers) |header| { |
| 437 | 443 | assert(header.name.len != 0); |
| 438 | try out.writeAll(header.name); | |
| 439 | try out.writeAll(": "); | |
| 440 | try out.writeAll(header.value); | |
| 441 | try out.writeAll("\r\n"); | |
| 444 | var bufs: [4][]const u8 = .{ header.name, ": ", header.value, "\r\n" }; | |
| 445 | try out.writeVecAll(&bufs); | |
| 442 | 446 | } |
| 443 | 447 | |
| 444 | 448 | try out.writeAll("\r\n"); |
| ... | ... | @@ -453,7 +457,13 @@ pub const Request = struct { |
| 453 | 457 | return if (elide_body) .{ |
| 454 | 458 | .http_protocol_output = request.server.out, |
| 455 | 459 | .state = state, |
| 456 | .writer = .discarding(buffer), | |
| 460 | .writer = .{ | |
| 461 | .buffer = buffer, | |
| 462 | .vtable = &.{ | |
| 463 | .drain = http.BodyWriter.elidingDrain, | |
| 464 | .sendFile = http.BodyWriter.elidingSendFile, | |
| 465 | }, | |
| 466 | }, | |
| 457 | 467 | } else .{ |
| 458 | 468 | .http_protocol_output = request.server.out, |
| 459 | 469 | .state = state, |
| ... | ... | @@ -484,10 +494,11 @@ pub const Request = struct { |
| 484 | 494 | none, |
| 485 | 495 | }; |
| 486 | 496 | |
| 497 | /// Does not invalidate `request.head`. | |
| 487 | 498 | pub fn upgradeRequested(request: *const Request) UpgradeRequest { |
| 488 | 499 | switch (request.head.version) { |
| 489 | .@"HTTP/1.0" => return null, | |
| 490 | .@"HTTP/1.1" => if (request.head.method != .GET) return null, | |
| 500 | .@"HTTP/1.0" => return .none, | |
| 501 | .@"HTTP/1.1" => if (request.head.method != .GET) return .none, | |
| 491 | 502 | } |
| 492 | 503 | |
| 493 | 504 | var sec_websocket_key: ?[]const u8 = null; |
| ... | ... | @@ -515,7 +526,7 @@ pub const Request = struct { |
| 515 | 526 | |
| 516 | 527 | /// The header is not guaranteed to be sent until `WebSocket.flush` is |
| 517 | 528 | /// called on the returned struct. |
| 518 | pub fn respondWebSocket(request: *Request, options: WebSocketOptions) Writer.Error!WebSocket { | |
| 529 | pub fn respondWebSocket(request: *Request, options: WebSocketOptions) ExpectContinueError!WebSocket { | |
| 519 | 530 | if (request.head.expect != null) return error.HttpExpectationFailed; |
| 520 | 531 | |
| 521 | 532 | const out = request.server.out; |
| ... | ... | @@ -534,16 +545,14 @@ pub const Request = struct { |
| 534 | 545 | try out.print("{s} {d} {s}\r\n", .{ @tagName(version), @intFromEnum(status), phrase }); |
| 535 | 546 | try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: "); |
| 536 | 547 | const base64_digest = try out.writableArray(28); |
| 537 | assert(std.base64.standard.Encoder.encode(&base64_digest, &digest).len == base64_digest.len); | |
| 548 | assert(std.base64.standard.Encoder.encode(base64_digest, &digest).len == base64_digest.len); | |
| 538 | 549 | out.advance(base64_digest.len); |
| 539 | 550 | try out.writeAll("\r\n"); |
| 540 | 551 | |
| 541 | 552 | for (options.extra_headers) |header| { |
| 542 | 553 | assert(header.name.len != 0); |
| 543 | try out.writeAll(header.name); | |
| 544 | try out.writeAll(": "); | |
| 545 | try out.writeAll(header.value); | |
| 546 | try out.writeAll("\r\n"); | |
| 554 | var bufs: [4][]const u8 = .{ header.name, ": ", header.value, "\r\n" }; | |
| 555 | try out.writeVecAll(&bufs); | |
| 547 | 556 | } |
| 548 | 557 | |
| 549 | 558 | try out.writeAll("\r\n"); |
| ... | ... | @@ -564,7 +573,7 @@ pub const Request = struct { |
| 564 | 573 | /// |
| 565 | 574 | /// See `readerExpectNone` for an infallible alternative that cannot write |
| 566 | 575 | /// to the server output stream. |
| 567 | pub fn readerExpectContinue(request: *Request, buffer: []u8) ExpectContinueError!*std.io.Reader { | |
| 576 | pub fn readerExpectContinue(request: *Request, buffer: []u8) ExpectContinueError!*Reader { | |
| 568 | 577 | const flush = request.head.expect != null; |
| 569 | 578 | try writeExpectContinue(request); |
| 570 | 579 | if (flush) try request.server.out.flush(); |
| ... | ... | @@ -576,9 +585,12 @@ pub const Request = struct { |
| 576 | 585 | /// this function. |
| 577 | 586 | /// |
| 578 | 587 | /// Asserts that this function is only called once. |
| 579 | pub fn readerExpectNone(request: *Request, buffer: []u8) *std.io.Reader { | |
| 588 | /// | |
| 589 | /// Invalidates the string memory inside `Head`. | |
| 590 | pub fn readerExpectNone(request: *Request, buffer: []u8) *Reader { | |
| 580 | 591 | assert(request.server.reader.state == .received_head); |
| 581 | 592 | assert(request.head.expect == null); |
| 593 | request.head.invalidateStrings(); | |
| 582 | 594 | if (!request.head.method.requestHasBody()) return .ending; |
| 583 | 595 | return request.server.reader.bodyReader(buffer, request.head.transfer_encoding, request.head.content_length); |
| 584 | 596 | } |
| ... | ... | @@ -640,7 +652,7 @@ pub const Request = struct { |
| 640 | 652 | /// See https://tools.ietf.org/html/rfc6455 |
| 641 | 653 | pub const WebSocket = struct { |
| 642 | 654 | key: []const u8, |
| 643 | input: *std.io.Reader, | |
| 655 | input: *Reader, | |
| 644 | 656 | output: *Writer, |
| 645 | 657 | |
| 646 | 658 | pub const Header0 = packed struct(u8) { |
| ... | ... | @@ -677,6 +689,8 @@ pub const WebSocket = struct { |
| 677 | 689 | UnexpectedOpCode, |
| 678 | 690 | MessageTooBig, |
| 679 | 691 | MissingMaskBit, |
| 692 | ReadFailed, | |
| 693 | EndOfStream, | |
| 680 | 694 | }; |
| 681 | 695 | |
| 682 | 696 | pub const SmallMessage = struct { |
| ... | ... | @@ -691,8 +705,9 @@ pub const WebSocket = struct { |
| 691 | 705 | pub fn readSmallMessage(ws: *WebSocket) ReadSmallTextMessageError!SmallMessage { |
| 692 | 706 | const in = ws.input; |
| 693 | 707 | while (true) { |
| 694 | const h0 = in.takeStruct(Header0); | |
| 695 | const h1 = in.takeStruct(Header1); | |
| 708 | const header = try in.takeArray(2); | |
| 709 | const h0: Header0 = @bitCast(header[0]); | |
| 710 | const h1: Header1 = @bitCast(header[1]); | |
| 696 | 711 | |
| 697 | 712 | switch (h0.opcode) { |
| 698 | 713 | .text, .binary, .pong, .ping => {}, |
| ... | ... | @@ -732,47 +747,49 @@ pub const WebSocket = struct { |
| 732 | 747 | } |
| 733 | 748 | |
| 734 | 749 | pub fn writeMessage(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void { |
| 735 | try writeMessageVecUnflushed(ws, &.{data}, op); | |
| 750 | var bufs: [1][]const u8 = .{data}; | |
| 751 | try writeMessageVecUnflushed(ws, &bufs, op); | |
| 736 | 752 | try ws.output.flush(); |
| 737 | 753 | } |
| 738 | 754 | |
| 739 | 755 | pub fn writeMessageUnflushed(ws: *WebSocket, data: []const u8, op: Opcode) Writer.Error!void { |
| 740 | try writeMessageVecUnflushed(ws, &.{data}, op); | |
| 756 | var bufs: [1][]const u8 = .{data}; | |
| 757 | try writeMessageVecUnflushed(ws, &bufs, op); | |
| 741 | 758 | } |
| 742 | 759 | |
| 743 | pub fn writeMessageVec(ws: *WebSocket, data: []const []const u8, op: Opcode) Writer.Error!void { | |
| 760 | pub fn writeMessageVec(ws: *WebSocket, data: [][]const u8, op: Opcode) Writer.Error!void { | |
| 744 | 761 | try writeMessageVecUnflushed(ws, data, op); |
| 745 | 762 | try ws.output.flush(); |
| 746 | 763 | } |
| 747 | 764 | |
| 748 | pub fn writeMessageVecUnflushed(ws: *WebSocket, data: []const []const u8, op: Opcode) Writer.Error!void { | |
| 765 | pub fn writeMessageVecUnflushed(ws: *WebSocket, data: [][]const u8, op: Opcode) Writer.Error!void { | |
| 749 | 766 | const total_len = l: { |
| 750 | 767 | var total_len: u64 = 0; |
| 751 | 768 | for (data) |iovec| total_len += iovec.len; |
| 752 | 769 | break :l total_len; |
| 753 | 770 | }; |
| 754 | 771 | const out = ws.output; |
| 755 | try out.writeStruct(@as(Header0, .{ | |
| 772 | try out.writeByte(@bitCast(@as(Header0, .{ | |
| 756 | 773 | .opcode = op, |
| 757 | 774 | .fin = true, |
| 758 | })); | |
| 775 | }))); | |
| 759 | 776 | switch (total_len) { |
| 760 | 0...125 => try out.writeStruct(@as(Header1, .{ | |
| 777 | 0...125 => try out.writeByte(@bitCast(@as(Header1, .{ | |
| 761 | 778 | .payload_len = @enumFromInt(total_len), |
| 762 | 779 | .mask = false, |
| 763 | })), | |
| 780 | }))), | |
| 764 | 781 | 126...0xffff => { |
| 765 | try out.writeStruct(@as(Header1, .{ | |
| 782 | try out.writeByte(@bitCast(@as(Header1, .{ | |
| 766 | 783 | .payload_len = .len16, |
| 767 | 784 | .mask = false, |
| 768 | })); | |
| 785 | }))); | |
| 769 | 786 | try out.writeInt(u16, @intCast(total_len), .big); |
| 770 | 787 | }, |
| 771 | 788 | else => { |
| 772 | try out.writeStruct(@as(Header1, .{ | |
| 789 | try out.writeByte(@bitCast(@as(Header1, .{ | |
| 773 | 790 | .payload_len = .len64, |
| 774 | 791 | .mask = false, |
| 775 | })); | |
| 792 | }))); | |
| 776 | 793 | try out.writeInt(u64, total_len, .big); |
| 777 | 794 | }, |
| 778 | 795 | } |
lib/std/http/test.zig+70-58| ... | ... | @@ -65,23 +65,22 @@ test "trailers" { |
| 65 | 65 | try req.sendBodiless(); |
| 66 | 66 | var response = try req.receiveHead(&.{}); |
| 67 | 67 | |
| 68 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 69 | defer gpa.free(body); | |
| 70 | ||
| 71 | try expectEqualStrings("Hello, World!\n", body); | |
| 72 | ||
| 73 | 68 | { |
| 74 | 69 | var it = response.head.iterateHeaders(); |
| 75 | 70 | const header = it.next().?; |
| 76 | try expect(!it.is_trailer); | |
| 77 | 71 | try expectEqualStrings("transfer-encoding", header.name); |
| 78 | 72 | try expectEqualStrings("chunked", header.value); |
| 79 | 73 | try expectEqual(null, it.next()); |
| 80 | 74 | } |
| 75 | ||
| 76 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 77 | defer gpa.free(body); | |
| 78 | ||
| 79 | try expectEqualStrings("Hello, World!\n", body); | |
| 80 | ||
| 81 | 81 | { |
| 82 | 82 | var it = response.iterateTrailers(); |
| 83 | 83 | const header = it.next().?; |
| 84 | try expect(it.is_trailer); | |
| 85 | 84 | try expectEqualStrings("X-Checksum", header.name); |
| 86 | 85 | try expectEqualStrings("aaaa", header.value); |
| 87 | 86 | try expectEqual(null, it.next()); |
| ... | ... | @@ -183,7 +182,11 @@ test "echo content server" { |
| 183 | 182 | if (request.head.expect) |expect_header_value| { |
| 184 | 183 | if (mem.eql(u8, expect_header_value, "garbage")) { |
| 185 | 184 | try expectError(error.HttpExpectationFailed, request.readerExpectContinue(&.{})); |
| 186 | try request.respond("", .{ .keep_alive = false }); | |
| 185 | request.head.expect = null; | |
| 186 | try request.respond("", .{ | |
| 187 | .keep_alive = false, | |
| 188 | .status = .expectation_failed, | |
| 189 | }); | |
| 187 | 190 | continue; |
| 188 | 191 | } |
| 189 | 192 | } |
| ... | ... | @@ -204,12 +207,14 @@ test "echo content server" { |
| 204 | 207 | // request.head.target, |
| 205 | 208 | //}); |
| 206 | 209 | |
| 207 | const body = try (try request.readerExpectContinue(&.{})).allocRemaining(std.testing.allocator, .limited(8192)); | |
| 210 | try expect(mem.startsWith(u8, request.head.target, "/echo-content")); | |
| 211 | try expectEqualStrings("text/plain", request.head.content_type.?); | |
| 212 | ||
| 213 | // head strings expire here | |
| 214 | const body = try (try request.readerExpectContinue(&.{})).allocRemaining(std.testing.allocator, .unlimited); | |
| 208 | 215 | defer std.testing.allocator.free(body); |
| 209 | 216 | |
| 210 | try expect(mem.startsWith(u8, request.head.target, "/echo-content")); | |
| 211 | 217 | try expectEqualStrings("Hello, World!\n", body); |
| 212 | try expectEqualStrings("text/plain", request.head.content_type.?); | |
| 213 | 218 | |
| 214 | 219 | var response = try request.respondStreaming(&.{}, .{ |
| 215 | 220 | .content_length = switch (request.head.transfer_encoding) { |
| ... | ... | @@ -273,7 +278,6 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" { |
| 273 | 278 | for (0..500) |i| { |
| 274 | 279 | try w.print("{d}, ah ha ha!\n", .{i}); |
| 275 | 280 | } |
| 276 | try expectEqual(7390, w.count); | |
| 277 | 281 | try w.flush(); |
| 278 | 282 | try response.end(); |
| 279 | 283 | try expectEqual(.closing, server.reader.state); |
| ... | ... | @@ -291,7 +295,7 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" { |
| 291 | 295 | |
| 292 | 296 | var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded |
| 293 | 297 | var stream_reader = stream.reader(&tiny_buffer); |
| 294 | const response = try stream_reader.interface().allocRemaining(gpa, .limited(8192)); | |
| 298 | const response = try stream_reader.interface().allocRemaining(gpa, .unlimited); | |
| 295 | 299 | defer gpa.free(response); |
| 296 | 300 | |
| 297 | 301 | var expected_response = std.ArrayList(u8).init(gpa); |
| ... | ... | @@ -362,7 +366,7 @@ test "receiving arbitrary http headers from the client" { |
| 362 | 366 | |
| 363 | 367 | var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded |
| 364 | 368 | var stream_reader = stream.reader(&tiny_buffer); |
| 365 | const response = try stream_reader.interface().allocRemaining(gpa, .limited(8192)); | |
| 369 | const response = try stream_reader.interface().allocRemaining(gpa, .unlimited); | |
| 366 | 370 | defer gpa.free(response); |
| 367 | 371 | |
| 368 | 372 | var expected_response = std.ArrayList(u8).init(gpa); |
| ... | ... | @@ -407,18 +411,19 @@ test "general client/server API coverage" { |
| 407 | 411 | |
| 408 | 412 | fn handleRequest(request: *http.Server.Request, listen_port: u16) !void { |
| 409 | 413 | const log = std.log.scoped(.server); |
| 414 | const gpa = std.testing.allocator; | |
| 410 | 415 | |
| 411 | log.info("{f} {s} {s}", .{ | |
| 412 | request.head.method, @tagName(request.head.version), request.head.target, | |
| 413 | }); | |
| 416 | log.info("{t} {t} {s}", .{ request.head.method, request.head.version, request.head.target }); | |
| 417 | const target = try gpa.dupe(u8, request.head.target); | |
| 418 | defer gpa.free(target); | |
| 414 | 419 | |
| 415 | const gpa = std.testing.allocator; | |
| 416 | const body = try (try request.readerExpectContinue(&.{})).allocRemaining(gpa, .limited(8192)); | |
| 420 | const reader = (try request.readerExpectContinue(&.{})); | |
| 421 | const body = try reader.allocRemaining(gpa, .unlimited); | |
| 417 | 422 | defer gpa.free(body); |
| 418 | 423 | |
| 419 | if (mem.startsWith(u8, request.head.target, "/get")) { | |
| 424 | if (mem.startsWith(u8, target, "/get")) { | |
| 420 | 425 | var response = try request.respondStreaming(&.{}, .{ |
| 421 | .content_length = if (mem.indexOf(u8, request.head.target, "?chunked") == null) | |
| 426 | .content_length = if (mem.indexOf(u8, target, "?chunked") == null) | |
| 422 | 427 | 14 |
| 423 | 428 | else |
| 424 | 429 | null, |
| ... | ... | @@ -433,7 +438,7 @@ test "general client/server API coverage" { |
| 433 | 438 | try w.writeAll("World!\n"); |
| 434 | 439 | try response.end(); |
| 435 | 440 | // Writing again would cause an assertion failure. |
| 436 | } else if (mem.startsWith(u8, request.head.target, "/large")) { | |
| 441 | } else if (mem.startsWith(u8, target, "/large")) { | |
| 437 | 442 | var response = try request.respondStreaming(&.{}, .{ |
| 438 | 443 | .content_length = 14 * 1024 + 14 * 10, |
| 439 | 444 | }); |
| ... | ... | @@ -447,7 +452,8 @@ test "general client/server API coverage" { |
| 447 | 452 | try w.writeAll("Hello, World!\n"); |
| 448 | 453 | } |
| 449 | 454 | |
| 450 | try w.writeAll("Hello, World!\n" ** 1024); | |
| 455 | var vec: [1][]const u8 = .{"Hello, World!\n"}; | |
| 456 | try w.writeSplatAll(&vec, 1024); | |
| 451 | 457 | |
| 452 | 458 | i = 0; |
| 453 | 459 | while (i < 5) : (i += 1) { |
| ... | ... | @@ -455,7 +461,7 @@ test "general client/server API coverage" { |
| 455 | 461 | } |
| 456 | 462 | |
| 457 | 463 | try response.end(); |
| 458 | } else if (mem.eql(u8, request.head.target, "/redirect/1")) { | |
| 464 | } else if (mem.eql(u8, target, "/redirect/1")) { | |
| 459 | 465 | var response = try request.respondStreaming(&.{}, .{ |
| 460 | 466 | .respond_options = .{ |
| 461 | 467 | .status = .found, |
| ... | ... | @@ -469,14 +475,14 @@ test "general client/server API coverage" { |
| 469 | 475 | try w.writeAll("Hello, "); |
| 470 | 476 | try w.writeAll("Redirected!\n"); |
| 471 | 477 | try response.end(); |
| 472 | } else if (mem.eql(u8, request.head.target, "/redirect/2")) { | |
| 478 | } else if (mem.eql(u8, target, "/redirect/2")) { | |
| 473 | 479 | try request.respond("Hello, Redirected!\n", .{ |
| 474 | 480 | .status = .found, |
| 475 | 481 | .extra_headers = &.{ |
| 476 | 482 | .{ .name = "location", .value = "/redirect/1" }, |
| 477 | 483 | }, |
| 478 | 484 | }); |
| 479 | } else if (mem.eql(u8, request.head.target, "/redirect/3")) { | |
| 485 | } else if (mem.eql(u8, target, "/redirect/3")) { | |
| 480 | 486 | const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/redirect/2", .{ |
| 481 | 487 | listen_port, |
| 482 | 488 | }); |
| ... | ... | @@ -488,23 +494,23 @@ test "general client/server API coverage" { |
| 488 | 494 | .{ .name = "location", .value = location }, |
| 489 | 495 | }, |
| 490 | 496 | }); |
| 491 | } else if (mem.eql(u8, request.head.target, "/redirect/4")) { | |
| 497 | } else if (mem.eql(u8, target, "/redirect/4")) { | |
| 492 | 498 | try request.respond("Hello, Redirected!\n", .{ |
| 493 | 499 | .status = .found, |
| 494 | 500 | .extra_headers = &.{ |
| 495 | 501 | .{ .name = "location", .value = "/redirect/3" }, |
| 496 | 502 | }, |
| 497 | 503 | }); |
| 498 | } else if (mem.eql(u8, request.head.target, "/redirect/5")) { | |
| 504 | } else if (mem.eql(u8, target, "/redirect/5")) { | |
| 499 | 505 | try request.respond("Hello, Redirected!\n", .{ |
| 500 | 506 | .status = .found, |
| 501 | 507 | .extra_headers = &.{ |
| 502 | 508 | .{ .name = "location", .value = "/%2525" }, |
| 503 | 509 | }, |
| 504 | 510 | }); |
| 505 | } else if (mem.eql(u8, request.head.target, "/%2525")) { | |
| 511 | } else if (mem.eql(u8, target, "/%2525")) { | |
| 506 | 512 | try request.respond("Encoded redirect successful!\n", .{}); |
| 507 | } else if (mem.eql(u8, request.head.target, "/redirect/invalid")) { | |
| 513 | } else if (mem.eql(u8, target, "/redirect/invalid")) { | |
| 508 | 514 | const invalid_port = try getUnusedTcpPort(); |
| 509 | 515 | const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}", .{invalid_port}); |
| 510 | 516 | defer gpa.free(location); |
| ... | ... | @@ -515,7 +521,7 @@ test "general client/server API coverage" { |
| 515 | 521 | .{ .name = "location", .value = location }, |
| 516 | 522 | }, |
| 517 | 523 | }); |
| 518 | } else if (mem.eql(u8, request.head.target, "/empty")) { | |
| 524 | } else if (mem.eql(u8, target, "/empty")) { | |
| 519 | 525 | try request.respond("", .{ |
| 520 | 526 | .extra_headers = &.{ |
| 521 | 527 | .{ .name = "empty", .value = "" }, |
| ... | ... | @@ -556,11 +562,12 @@ test "general client/server API coverage" { |
| 556 | 562 | try req.sendBodiless(); |
| 557 | 563 | var response = try req.receiveHead(&redirect_buffer); |
| 558 | 564 | |
| 559 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 565 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 566 | ||
| 567 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 560 | 568 | defer gpa.free(body); |
| 561 | 569 | |
| 562 | 570 | try expectEqualStrings("Hello, World!\n", body); |
| 563 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 564 | 571 | } |
| 565 | 572 | |
| 566 | 573 | // connection has been kept alive |
| ... | ... | @@ -579,7 +586,7 @@ test "general client/server API coverage" { |
| 579 | 586 | try req.sendBodiless(); |
| 580 | 587 | var response = try req.receiveHead(&redirect_buffer); |
| 581 | 588 | |
| 582 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192 * 1024)); | |
| 589 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 583 | 590 | defer gpa.free(body); |
| 584 | 591 | |
| 585 | 592 | try expectEqual(@as(usize, 14 * 1024 + 14 * 10), body.len); |
| ... | ... | @@ -601,12 +608,13 @@ test "general client/server API coverage" { |
| 601 | 608 | try req.sendBodiless(); |
| 602 | 609 | var response = try req.receiveHead(&redirect_buffer); |
| 603 | 610 | |
| 604 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 611 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 612 | try expectEqual(14, response.head.content_length.?); | |
| 613 | ||
| 614 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 605 | 615 | defer gpa.free(body); |
| 606 | 616 | |
| 607 | 617 | try expectEqualStrings("", body); |
| 608 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 609 | try expectEqual(14, response.head.content_length.?); | |
| 610 | 618 | } |
| 611 | 619 | |
| 612 | 620 | // connection has been kept alive |
| ... | ... | @@ -625,11 +633,12 @@ test "general client/server API coverage" { |
| 625 | 633 | try req.sendBodiless(); |
| 626 | 634 | var response = try req.receiveHead(&redirect_buffer); |
| 627 | 635 | |
| 628 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 636 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 637 | ||
| 638 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 629 | 639 | defer gpa.free(body); |
| 630 | 640 | |
| 631 | 641 | try expectEqualStrings("Hello, World!\n", body); |
| 632 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 633 | 642 | } |
| 634 | 643 | |
| 635 | 644 | // connection has been kept alive |
| ... | ... | @@ -648,12 +657,13 @@ test "general client/server API coverage" { |
| 648 | 657 | try req.sendBodiless(); |
| 649 | 658 | var response = try req.receiveHead(&redirect_buffer); |
| 650 | 659 | |
| 651 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 660 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 661 | try expect(response.head.transfer_encoding == .chunked); | |
| 662 | ||
| 663 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 652 | 664 | defer gpa.free(body); |
| 653 | 665 | |
| 654 | 666 | try expectEqualStrings("", body); |
| 655 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 656 | try expect(response.head.transfer_encoding == .chunked); | |
| 657 | 667 | } |
| 658 | 668 | |
| 659 | 669 | // connection has been kept alive |
| ... | ... | @@ -674,11 +684,12 @@ test "general client/server API coverage" { |
| 674 | 684 | try req.sendBodiless(); |
| 675 | 685 | var response = try req.receiveHead(&redirect_buffer); |
| 676 | 686 | |
| 677 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 687 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 688 | ||
| 689 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 678 | 690 | defer gpa.free(body); |
| 679 | 691 | |
| 680 | 692 | try expectEqualStrings("Hello, World!\n", body); |
| 681 | try expectEqualStrings("text/plain", response.head.content_type.?); | |
| 682 | 693 | } |
| 683 | 694 | |
| 684 | 695 | // connection has been closed |
| ... | ... | @@ -703,11 +714,6 @@ test "general client/server API coverage" { |
| 703 | 714 | |
| 704 | 715 | try std.testing.expectEqual(.ok, response.head.status); |
| 705 | 716 | |
| 706 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 707 | defer gpa.free(body); | |
| 708 | ||
| 709 | try expectEqualStrings("", body); | |
| 710 | ||
| 711 | 717 | var it = response.head.iterateHeaders(); |
| 712 | 718 | { |
| 713 | 719 | const header = it.next().?; |
| ... | ... | @@ -715,6 +721,12 @@ test "general client/server API coverage" { |
| 715 | 721 | try expectEqualStrings("content-length", header.name); |
| 716 | 722 | try expectEqualStrings("0", header.value); |
| 717 | 723 | } |
| 724 | ||
| 725 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 726 | defer gpa.free(body); | |
| 727 | ||
| 728 | try expectEqualStrings("", body); | |
| 729 | ||
| 718 | 730 | { |
| 719 | 731 | const header = it.next().?; |
| 720 | 732 | try expect(!it.is_trailer); |
| ... | ... | @@ -740,7 +752,7 @@ test "general client/server API coverage" { |
| 740 | 752 | try req.sendBodiless(); |
| 741 | 753 | var response = try req.receiveHead(&redirect_buffer); |
| 742 | 754 | |
| 743 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 755 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 744 | 756 | defer gpa.free(body); |
| 745 | 757 | |
| 746 | 758 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -762,7 +774,7 @@ test "general client/server API coverage" { |
| 762 | 774 | try req.sendBodiless(); |
| 763 | 775 | var response = try req.receiveHead(&redirect_buffer); |
| 764 | 776 | |
| 765 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 777 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 766 | 778 | defer gpa.free(body); |
| 767 | 779 | |
| 768 | 780 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -784,7 +796,7 @@ test "general client/server API coverage" { |
| 784 | 796 | try req.sendBodiless(); |
| 785 | 797 | var response = try req.receiveHead(&redirect_buffer); |
| 786 | 798 | |
| 787 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 799 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 788 | 800 | defer gpa.free(body); |
| 789 | 801 | |
| 790 | 802 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -825,7 +837,7 @@ test "general client/server API coverage" { |
| 825 | 837 | try req.sendBodiless(); |
| 826 | 838 | var response = try req.receiveHead(&redirect_buffer); |
| 827 | 839 | |
| 828 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 840 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 829 | 841 | defer gpa.free(body); |
| 830 | 842 | |
| 831 | 843 | try expectEqualStrings("Encoded redirect successful!\n", body); |
| ... | ... | @@ -915,7 +927,7 @@ test "Server streams both reading and writing" { |
| 915 | 927 | try body_writer.writer.writeAll("fish"); |
| 916 | 928 | try body_writer.end(); |
| 917 | 929 | |
| 918 | const body = try response.reader(&.{}).allocRemaining(std.testing.allocator, .limited(8192)); | |
| 930 | const body = try response.reader(&.{}).allocRemaining(std.testing.allocator, .unlimited); | |
| 919 | 931 | defer std.testing.allocator.free(body); |
| 920 | 932 | |
| 921 | 933 | try expectEqualStrings("ONE FISH", body); |
| ... | ... | @@ -947,7 +959,7 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 947 | 959 | |
| 948 | 960 | var response = try req.receiveHead(&redirect_buffer); |
| 949 | 961 | |
| 950 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 962 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 951 | 963 | defer gpa.free(body); |
| 952 | 964 | |
| 953 | 965 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -980,7 +992,7 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 980 | 992 | |
| 981 | 993 | var response = try req.receiveHead(&redirect_buffer); |
| 982 | 994 | |
| 983 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 995 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 984 | 996 | defer gpa.free(body); |
| 985 | 997 | |
| 986 | 998 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -1034,7 +1046,7 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1034 | 1046 | var response = try req.receiveHead(&redirect_buffer); |
| 1035 | 1047 | try expectEqual(.ok, response.head.status); |
| 1036 | 1048 | |
| 1037 | const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192)); | |
| 1049 | const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); | |
| 1038 | 1050 | defer gpa.free(body); |
| 1039 | 1051 | |
| 1040 | 1052 | try expectEqualStrings("Hello, World!\n", body); |
| ... | ... | @@ -1175,7 +1187,7 @@ test "redirect to different connection" { |
| 1175 | 1187 | var response = try req.receiveHead(&redirect_buffer); |
| 1176 | 1188 | var reader = response.reader(&.{}); |
| 1177 | 1189 | |
| 1178 | const body = try reader.allocRemaining(gpa, .limited(8192)); | |
| 1190 | const body = try reader.allocRemaining(gpa, .unlimited); | |
| 1179 | 1191 | defer gpa.free(body); |
| 1180 | 1192 | |
| 1181 | 1193 | try expectEqualStrings("good job, you pass", body); |
lib/std/math/big/int.zig+3-5| ... | ... | @@ -1710,7 +1710,7 @@ pub const Mutable = struct { |
| 1710 | 1710 | |
| 1711 | 1711 | if (xy_trailing != 0 and r.limbs[r.len - 1] != 0) { |
| 1712 | 1712 | // Manually shift here since we know its limb aligned. |
| 1713 | mem.copyBackwards(Limb, r.limbs[xy_trailing..], r.limbs[0..r.len]); | |
| 1713 | @memmove(r.limbs[xy_trailing..][0..r.len], r.limbs[0..r.len]); | |
| 1714 | 1714 | @memset(r.limbs[0..xy_trailing], 0); |
| 1715 | 1715 | r.len += xy_trailing; |
| 1716 | 1716 | } |
| ... | ... | @@ -3836,8 +3836,7 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) usize { |
| 3836 | 3836 | std.debug.assert(@intFromPtr(r.ptr) >= @intFromPtr(a.ptr)); |
| 3837 | 3837 | |
| 3838 | 3838 | if (shift == 0) { |
| 3839 | if (a.ptr != r.ptr) | |
| 3840 | std.mem.copyBackwards(Limb, r[0..a.len], a); | |
| 3839 | if (a.ptr != r.ptr) @memmove(r[0..a.len], a); | |
| 3841 | 3840 | return a.len; |
| 3842 | 3841 | } |
| 3843 | 3842 | if (shift >= limb_bits) { |
| ... | ... | @@ -3891,8 +3890,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) usize { |
| 3891 | 3890 | if (shift == 0) { |
| 3892 | 3891 | std.debug.assert(r.len >= a.len); |
| 3893 | 3892 | |
| 3894 | if (a.ptr != r.ptr) | |
| 3895 | std.mem.copyForwards(Limb, r[0..a.len], a); | |
| 3893 | if (a.ptr != r.ptr) @memmove(r[0..a.len], a); | |
| 3896 | 3894 | return a.len; |
| 3897 | 3895 | } |
| 3898 | 3896 | if (shift >= limb_bits) { |
lib/std/meta.zig+1-1| ... | ... | @@ -939,7 +939,7 @@ fn CreateUniqueTuple(comptime N: comptime_int, comptime types: [N]type) type { |
| 939 | 939 | .type = T, |
| 940 | 940 | .default_value_ptr = null, |
| 941 | 941 | .is_comptime = false, |
| 942 | .alignment = 0, | |
| 942 | .alignment = @alignOf(T), | |
| 943 | 943 | }; |
| 944 | 944 | } |
| 945 | 945 |
lib/std/net.zig+1-1| ... | ... | @@ -1944,7 +1944,7 @@ pub const Stream = struct { |
| 1944 | 1944 | pub const Error = ReadError; |
| 1945 | 1945 | |
| 1946 | 1946 | pub fn getStream(r: *const Reader) Stream { |
| 1947 | return r.stream; | |
| 1947 | return r.net_stream; | |
| 1948 | 1948 | } |
| 1949 | 1949 | |
| 1950 | 1950 | pub fn getError(r: *const Reader) ?Error { |
lib/std/os/linux.zig+38| ... | ... | @@ -1014,6 +1014,44 @@ pub fn munmap(address: [*]const u8, length: usize) usize { |
| 1014 | 1014 | return syscall2(.munmap, @intFromPtr(address), length); |
| 1015 | 1015 | } |
| 1016 | 1016 | |
| 1017 | pub fn mlock(address: [*]const u8, length: usize) usize { | |
| 1018 | return syscall2(.mlock, @intFromPtr(address), length); | |
| 1019 | } | |
| 1020 | ||
| 1021 | pub fn munlock(address: [*]const u8, length: usize) usize { | |
| 1022 | return syscall2(.munlock, @intFromPtr(address), length); | |
| 1023 | } | |
| 1024 | ||
| 1025 | pub const MLOCK = packed struct(u32) { | |
| 1026 | ONFAULT: bool = false, | |
| 1027 | _1: u31 = 0, | |
| 1028 | }; | |
| 1029 | ||
| 1030 | pub fn mlock2(address: [*]const u8, length: usize, flags: MLOCK) usize { | |
| 1031 | return syscall3(.mlock2, @intFromPtr(address), length, @as(u32, @bitCast(flags))); | |
| 1032 | } | |
| 1033 | ||
| 1034 | pub const MCL = if (native_arch.isSPARC() or native_arch.isPowerPC()) packed struct(u32) { | |
| 1035 | _0: u13 = 0, | |
| 1036 | CURRENT: bool = false, | |
| 1037 | FUTURE: bool = false, | |
| 1038 | ONFAULT: bool = false, | |
| 1039 | _4: u16 = 0, | |
| 1040 | } else packed struct(u32) { | |
| 1041 | CURRENT: bool = false, | |
| 1042 | FUTURE: bool = false, | |
| 1043 | ONFAULT: bool = false, | |
| 1044 | _3: u29 = 0, | |
| 1045 | }; | |
| 1046 | ||
| 1047 | pub fn mlockall(flags: MCL) usize { | |
| 1048 | return syscall1(.mlockall, @as(u32, @bitCast(flags))); | |
| 1049 | } | |
| 1050 | ||
| 1051 | pub fn munlockall() usize { | |
| 1052 | return syscall0(.munlockall); | |
| 1053 | } | |
| 1054 | ||
| 1017 | 1055 | pub fn poll(fds: [*]pollfd, n: nfds_t, timeout: i32) usize { |
| 1018 | 1056 | if (@hasField(SYS, "poll")) { |
| 1019 | 1057 | return syscall3(.poll, @intFromPtr(fds), n, @as(u32, @bitCast(timeout))); |
lib/std/os/windows.zig+3-8| ... | ... | @@ -1332,7 +1332,7 @@ pub fn GetFinalPathNameByHandle( |
| 1332 | 1332 | // dropping the \Device\Mup\ and making sure the path begins with \\ |
| 1333 | 1333 | if (mem.eql(u16, device_name_u16, std.unicode.utf8ToUtf16LeStringLiteral("Mup"))) { |
| 1334 | 1334 | out_buffer[0] = '\\'; |
| 1335 | mem.copyForwards(u16, out_buffer[1..][0..file_name_u16.len], file_name_u16); | |
| 1335 | @memmove(out_buffer[1..][0..file_name_u16.len], file_name_u16); | |
| 1336 | 1336 | return out_buffer[0 .. 1 + file_name_u16.len]; |
| 1337 | 1337 | } |
| 1338 | 1338 | |
| ... | ... | @@ -1400,7 +1400,7 @@ pub fn GetFinalPathNameByHandle( |
| 1400 | 1400 | if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong; |
| 1401 | 1401 | |
| 1402 | 1402 | @memcpy(out_buffer[0..drive_letter.len], drive_letter); |
| 1403 | mem.copyForwards(u16, out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16); | |
| 1403 | @memmove(out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16); | |
| 1404 | 1404 | const total_len = drive_letter.len + file_name_u16.len; |
| 1405 | 1405 | |
| 1406 | 1406 | // Validate that DOS does not contain any spurious nul bytes. |
| ... | ... | @@ -1449,12 +1449,7 @@ pub fn GetFinalPathNameByHandle( |
| 1449 | 1449 | // to copy backwards. We also need to do this before copying the volume path because |
| 1450 | 1450 | // it could overwrite the file_name_u16 memory. |
| 1451 | 1451 | const file_name_dest = out_buffer[volume_path.len..][0..file_name_u16.len]; |
| 1452 | const file_name_byte_offset = @intFromPtr(file_name_u16.ptr) - @intFromPtr(out_buffer.ptr); | |
| 1453 | const file_name_index = file_name_byte_offset / @sizeOf(u16); | |
| 1454 | if (volume_path.len > file_name_index) | |
| 1455 | mem.copyBackwards(u16, file_name_dest, file_name_u16) | |
| 1456 | else | |
| 1457 | mem.copyForwards(u16, file_name_dest, file_name_u16); | |
| 1452 | @memmove(file_name_dest, file_name_u16); | |
| 1458 | 1453 | @memcpy(out_buffer[0..volume_path.len], volume_path); |
| 1459 | 1454 | const total_len = volume_path.len + file_name_u16.len; |
| 1460 | 1455 |
lib/std/process/Child.zig-9| ... | ... | @@ -901,11 +901,6 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void { |
| 901 | 901 | if (dir_buf.items.len > 0) try dir_buf.append(self.allocator, fs.path.sep); |
| 902 | 902 | try dir_buf.appendSlice(self.allocator, app_dir); |
| 903 | 903 | } |
| 904 | if (dir_buf.items.len > 0) { | |
| 905 | // Need to normalize the path, openDirW can't handle things like double backslashes | |
| 906 | const normalized_len = windows.normalizePath(u16, dir_buf.items) catch return error.BadPathName; | |
| 907 | dir_buf.shrinkRetainingCapacity(normalized_len); | |
| 908 | } | |
| 909 | 904 | |
| 910 | 905 | windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| { |
| 911 | 906 | const original_err = switch (no_path_err) { |
| ... | ... | @@ -930,10 +925,6 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void { |
| 930 | 925 | while (it.next()) |search_path| { |
| 931 | 926 | dir_buf.clearRetainingCapacity(); |
| 932 | 927 | try dir_buf.appendSlice(self.allocator, search_path); |
| 933 | // Need to normalize the path, some PATH values can contain things like double | |
| 934 | // backslashes which openDirW can't handle | |
| 935 | const normalized_len = windows.normalizePath(u16, dir_buf.items) catch continue; | |
| 936 | dir_buf.shrinkRetainingCapacity(normalized_len); | |
| 937 | 928 | |
| 938 | 929 | if (windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) { |
| 939 | 930 | break :run; |
lib/std/std.zig-2| ... | ... | @@ -9,8 +9,6 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged; |
| 9 | 9 | pub const AutoHashMap = hash_map.AutoHashMap; |
| 10 | 10 | pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; |
| 11 | 11 | pub const BitStack = @import("BitStack.zig"); |
| 12 | pub const BoundedArray = @import("bounded_array.zig").BoundedArray; | |
| 13 | pub const BoundedArrayAligned = @import("bounded_array.zig").BoundedArrayAligned; | |
| 14 | 12 | pub const Build = @import("Build.zig"); |
| 15 | 13 | pub const BufMap = @import("buf_map.zig").BufMap; |
| 16 | 14 | pub const BufSet = @import("buf_set.zig").BufSet; |
lib/std/zig/AstGen.zig+3| ... | ... | @@ -5386,6 +5386,9 @@ fn unionDeclInner( |
| 5386 | 5386 | return astgen.failNode(member_node, "union field missing type", .{}); |
| 5387 | 5387 | } |
| 5388 | 5388 | if (member.ast.align_expr.unwrap()) |align_expr| { |
| 5389 | if (layout == .@"packed") { | |
| 5390 | return astgen.failNode(align_expr, "unable to override alignment of packed union fields", .{}); | |
| 5391 | } | |
| 5389 | 5392 | const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, align_expr); |
| 5390 | 5393 | wip_members.appendToField(@intFromEnum(align_inst)); |
| 5391 | 5394 | any_aligned_fields = true; |
lib/std/zig/llvm/Builder.zig+4-3| ... | ... | @@ -8533,18 +8533,19 @@ pub const Metadata = enum(u32) { |
| 8533 | 8533 | .type = []const u8, |
| 8534 | 8534 | .default_value_ptr = null, |
| 8535 | 8535 | .is_comptime = false, |
| 8536 | .alignment = 0, | |
| 8536 | .alignment = @alignOf([]const u8), | |
| 8537 | 8537 | }; |
| 8538 | 8538 | } |
| 8539 | 8539 | fmt_str = fmt_str ++ "("; |
| 8540 | 8540 | inline for (fields[2..], names) |*field, name| { |
| 8541 | 8541 | fmt_str = fmt_str ++ "{[" ++ name ++ "]f}"; |
| 8542 | const T = std.fmt.Formatter(FormatData, format); | |
| 8542 | 8543 | field.* = .{ |
| 8543 | 8544 | .name = name, |
| 8544 | .type = std.fmt.Formatter(FormatData, format), | |
| 8545 | .type = T, | |
| 8545 | 8546 | .default_value_ptr = null, |
| 8546 | 8547 | .is_comptime = false, |
| 8547 | .alignment = 0, | |
| 8548 | .alignment = @alignOf(T), | |
| 8548 | 8549 | }; |
| 8549 | 8550 | } |
| 8550 | 8551 | fmt_str = fmt_str ++ ")\n"; |
src/Compilation.zig+86-52| ... | ... | @@ -2103,6 +2103,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2103 | 2103 | .local_zir_cache = local_zir_cache, |
| 2104 | 2104 | .error_limit = error_limit, |
| 2105 | 2105 | .llvm_object = null, |
| 2106 | .analysis_roots_buffer = undefined, | |
| 2107 | .analysis_roots_len = 0, | |
| 2106 | 2108 | }; |
| 2107 | 2109 | try zcu.init(options.thread_pool.getIdCount()); |
| 2108 | 2110 | break :blk zcu; |
| ... | ... | @@ -2183,8 +2185,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2183 | 2185 | .emit_docs = try options.emit_docs.resolve(arena, &options, .docs), |
| 2184 | 2186 | }; |
| 2185 | 2187 | |
| 2186 | comp.windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{}); | |
| 2187 | errdefer comp.windows_libs.deinit(gpa); | |
| 2188 | errdefer { | |
| 2189 | for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); | |
| 2190 | comp.windows_libs.deinit(gpa); | |
| 2191 | } | |
| 2192 | try comp.windows_libs.ensureUnusedCapacity(gpa, options.windows_lib_names.len); | |
| 2193 | for (options.windows_lib_names) |windows_lib| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, windows_lib), {}); | |
| 2188 | 2194 | |
| 2189 | 2195 | // Prevent some footguns by making the "any" fields of config reflect |
| 2190 | 2196 | // the default Module settings. |
| ... | ... | @@ -2378,7 +2384,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2378 | 2384 | }; |
| 2379 | 2385 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); |
| 2380 | 2386 | } |
| 2381 | comp.link_task_queue.pending_prelink_tasks += @intCast(comp.c_object_table.count()); | |
| 2382 | 2387 | |
| 2383 | 2388 | // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`. |
| 2384 | 2389 | const win32_resource_count = |
| ... | ... | @@ -2386,10 +2391,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2386 | 2391 | if (win32_resource_count > 0) { |
| 2387 | 2392 | dev.check(.win32_resource); |
| 2388 | 2393 | try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count); |
| 2389 | // Add this after adding logic to updateWin32Resource to pass the | |
| 2390 | // result into link.loadInput. loadInput integration is not implemented | |
| 2391 | // for Windows linking logic yet. | |
| 2392 | //comp.link_task_queue.pending_prelink_tasks += @intCast(win32_resource_count); | |
| 2393 | 2394 | for (options.rc_source_files) |rc_source_file| { |
| 2394 | 2395 | const win32_resource = try gpa.create(Win32Resource); |
| 2395 | 2396 | errdefer gpa.destroy(win32_resource); |
| ... | ... | @@ -2415,13 +2416,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2415 | 2416 | |
| 2416 | 2417 | if (comp.emit_bin != null and target.ofmt != .c) { |
| 2417 | 2418 | if (!comp.skip_linker_dependencies) { |
| 2418 | // These DLLs are always loaded into every Windows process. | |
| 2419 | if (target.os.tag == .windows and is_exe_or_dyn_lib) { | |
| 2420 | try comp.windows_libs.ensureUnusedCapacity(gpa, 2); | |
| 2421 | comp.windows_libs.putAssumeCapacity("kernel32", {}); | |
| 2422 | comp.windows_libs.putAssumeCapacity("ntdll", {}); | |
| 2423 | } | |
| 2424 | ||
| 2425 | 2419 | // If we need to build libc for the target, add work items for it. |
| 2426 | 2420 | // We go through the work queue so that building can be done in parallel. |
| 2427 | 2421 | // If linking against host libc installation, instead queue up jobs |
| ... | ... | @@ -2455,62 +2449,51 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2455 | 2449 | |
| 2456 | 2450 | if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| { |
| 2457 | 2451 | comp.queued_jobs.musl_crt_file[@intFromEnum(f)] = true; |
| 2458 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2459 | 2452 | } |
| 2460 | 2453 | switch (comp.config.link_mode) { |
| 2461 | 2454 | .static => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_a)] = true, |
| 2462 | 2455 | .dynamic => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_so)] = true, |
| 2463 | 2456 | } |
| 2464 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2465 | 2457 | } else if (target.isGnuLibC()) { |
| 2466 | 2458 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 2467 | 2459 | |
| 2468 | 2460 | if (glibc.needsCrt0(comp.config.output_mode)) |f| { |
| 2469 | 2461 | comp.queued_jobs.glibc_crt_file[@intFromEnum(f)] = true; |
| 2470 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2471 | 2462 | } |
| 2472 | 2463 | comp.queued_jobs.glibc_shared_objects = true; |
| 2473 | comp.link_task_queue.pending_prelink_tasks += glibc.sharedObjectsCount(target); | |
| 2474 | 2464 | |
| 2475 | 2465 | comp.queued_jobs.glibc_crt_file[@intFromEnum(glibc.CrtFile.libc_nonshared_a)] = true; |
| 2476 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2477 | 2466 | } else if (target.isFreeBSDLibC()) { |
| 2478 | 2467 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 2479 | 2468 | |
| 2480 | 2469 | if (freebsd.needsCrt0(comp.config.output_mode)) |f| { |
| 2481 | 2470 | comp.queued_jobs.freebsd_crt_file[@intFromEnum(f)] = true; |
| 2482 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2483 | 2471 | } |
| 2484 | 2472 | |
| 2485 | 2473 | comp.queued_jobs.freebsd_shared_objects = true; |
| 2486 | comp.link_task_queue.pending_prelink_tasks += freebsd.sharedObjectsCount(); | |
| 2487 | 2474 | } else if (target.isNetBSDLibC()) { |
| 2488 | 2475 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 2489 | 2476 | |
| 2490 | 2477 | if (netbsd.needsCrt0(comp.config.output_mode)) |f| { |
| 2491 | 2478 | comp.queued_jobs.netbsd_crt_file[@intFromEnum(f)] = true; |
| 2492 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2493 | 2479 | } |
| 2494 | 2480 | |
| 2495 | 2481 | comp.queued_jobs.netbsd_shared_objects = true; |
| 2496 | comp.link_task_queue.pending_prelink_tasks += netbsd.sharedObjectsCount(); | |
| 2497 | 2482 | } else if (target.isWasiLibC()) { |
| 2498 | 2483 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 2499 | 2484 | |
| 2500 | 2485 | comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true; |
| 2501 | 2486 | comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true; |
| 2502 | comp.link_task_queue.pending_prelink_tasks += 2; | |
| 2503 | 2487 | } else if (target.isMinGW()) { |
| 2504 | 2488 | if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable; |
| 2505 | 2489 | |
| 2506 | 2490 | const main_crt_file: mingw.CrtFile = if (is_dyn_lib) .dllcrt2_o else .crt2_o; |
| 2507 | 2491 | comp.queued_jobs.mingw_crt_file[@intFromEnum(main_crt_file)] = true; |
| 2508 | 2492 | comp.queued_jobs.mingw_crt_file[@intFromEnum(mingw.CrtFile.libmingw32_lib)] = true; |
| 2509 | comp.link_task_queue.pending_prelink_tasks += 2; | |
| 2510 | 2493 | |
| 2511 | 2494 | // When linking mingw-w64 there are some import libs we always need. |
| 2512 | 2495 | try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len); |
| 2513 | for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {}); | |
| 2496 | for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(try gpa.dupe(u8, name), {}); | |
| 2514 | 2497 | } else { |
| 2515 | 2498 | return error.LibCUnavailable; |
| 2516 | 2499 | } |
| ... | ... | @@ -2520,7 +2503,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2520 | 2503 | target.isMinGW()) |
| 2521 | 2504 | { |
| 2522 | 2505 | comp.queued_jobs.zigc_lib = true; |
| 2523 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2524 | 2506 | } |
| 2525 | 2507 | } |
| 2526 | 2508 | |
| ... | ... | @@ -2537,50 +2519,41 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2537 | 2519 | } |
| 2538 | 2520 | if (comp.wantBuildLibUnwindFromSource()) { |
| 2539 | 2521 | comp.queued_jobs.libunwind = true; |
| 2540 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2541 | 2522 | } |
| 2542 | 2523 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { |
| 2543 | 2524 | comp.queued_jobs.libcxx = true; |
| 2544 | 2525 | comp.queued_jobs.libcxxabi = true; |
| 2545 | comp.link_task_queue.pending_prelink_tasks += 2; | |
| 2546 | 2526 | } |
| 2547 | 2527 | if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) { |
| 2548 | 2528 | comp.queued_jobs.libtsan = true; |
| 2549 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2550 | 2529 | } |
| 2551 | 2530 | |
| 2552 | 2531 | if (can_build_compiler_rt) { |
| 2553 | 2532 | if (comp.compiler_rt_strat == .lib) { |
| 2554 | 2533 | log.debug("queuing a job to build compiler_rt_lib", .{}); |
| 2555 | 2534 | comp.queued_jobs.compiler_rt_lib = true; |
| 2556 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2557 | 2535 | } else if (comp.compiler_rt_strat == .obj) { |
| 2558 | 2536 | log.debug("queuing a job to build compiler_rt_obj", .{}); |
| 2559 | 2537 | // In this case we are making a static library, so we ask |
| 2560 | 2538 | // for a compiler-rt object to put in it. |
| 2561 | 2539 | comp.queued_jobs.compiler_rt_obj = true; |
| 2562 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2563 | 2540 | } else if (comp.compiler_rt_strat == .dyn_lib) { |
| 2564 | 2541 | // hack for stage2_x86_64 + coff |
| 2565 | 2542 | log.debug("queuing a job to build compiler_rt_dyn_lib", .{}); |
| 2566 | 2543 | comp.queued_jobs.compiler_rt_dyn_lib = true; |
| 2567 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2568 | 2544 | } |
| 2569 | 2545 | |
| 2570 | 2546 | if (comp.ubsan_rt_strat == .lib) { |
| 2571 | 2547 | log.debug("queuing a job to build ubsan_rt_lib", .{}); |
| 2572 | 2548 | comp.queued_jobs.ubsan_rt_lib = true; |
| 2573 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2574 | 2549 | } else if (comp.ubsan_rt_strat == .obj) { |
| 2575 | 2550 | log.debug("queuing a job to build ubsan_rt_obj", .{}); |
| 2576 | 2551 | comp.queued_jobs.ubsan_rt_obj = true; |
| 2577 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2578 | 2552 | } |
| 2579 | 2553 | |
| 2580 | 2554 | if (is_exe_or_dyn_lib and comp.config.any_fuzz) { |
| 2581 | 2555 | log.debug("queuing a job to build libfuzzer", .{}); |
| 2582 | 2556 | comp.queued_jobs.fuzzer_lib = true; |
| 2583 | comp.link_task_queue.pending_prelink_tasks += 1; | |
| 2584 | 2557 | } |
| 2585 | 2558 | } |
| 2586 | 2559 | } |
| ... | ... | @@ -2588,8 +2561,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil |
| 2588 | 2561 | try comp.link_task_queue.queued_prelink.append(gpa, .load_explicitly_provided); |
| 2589 | 2562 | } |
| 2590 | 2563 | log.debug("queued prelink tasks: {d}", .{comp.link_task_queue.queued_prelink.items.len}); |
| 2591 | log.debug("pending prelink tasks: {d}", .{comp.link_task_queue.pending_prelink_tasks}); | |
| 2592 | ||
| 2593 | 2564 | return comp; |
| 2594 | 2565 | } |
| 2595 | 2566 | |
| ... | ... | @@ -2608,6 +2579,7 @@ pub fn destroy(comp: *Compilation) void { |
| 2608 | 2579 | comp.c_object_work_queue.deinit(); |
| 2609 | 2580 | comp.win32_resource_work_queue.deinit(); |
| 2610 | 2581 | |
| 2582 | for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib); | |
| 2611 | 2583 | comp.windows_libs.deinit(gpa); |
| 2612 | 2584 | |
| 2613 | 2585 | { |
| ... | ... | @@ -2933,22 +2905,26 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { |
| 2933 | 2905 | try comp.appendFileSystemInput(embed_file.path); |
| 2934 | 2906 | } |
| 2935 | 2907 | |
| 2936 | zcu.analysis_roots.clear(); | |
| 2908 | zcu.analysis_roots_len = 0; | |
| 2937 | 2909 | |
| 2938 | zcu.analysis_roots.appendAssumeCapacity(zcu.std_mod); | |
| 2910 | zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.std_mod; | |
| 2911 | zcu.analysis_roots_len += 1; | |
| 2939 | 2912 | |
| 2940 | 2913 | // Normally we rely on importing std to in turn import the root source file in the start code. |
| 2941 | 2914 | // However, the main module is distinct from the root module in tests, so that won't happen there. |
| 2942 | 2915 | if (comp.config.is_test and zcu.main_mod != zcu.std_mod) { |
| 2943 | zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod); | |
| 2916 | zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zcu.main_mod; | |
| 2917 | zcu.analysis_roots_len += 1; | |
| 2944 | 2918 | } |
| 2945 | 2919 | |
| 2946 | 2920 | if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| { |
| 2947 | zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod); | |
| 2921 | zcu.analysis_roots_buffer[zcu.analysis_roots_len] = compiler_rt_mod; | |
| 2922 | zcu.analysis_roots_len += 1; | |
| 2948 | 2923 | } |
| 2949 | 2924 | |
| 2950 | 2925 | if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| { |
| 2951 | zcu.analysis_roots.appendAssumeCapacity(ubsan_rt_mod); | |
| 2926 | zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod; | |
| 2927 | zcu.analysis_roots_len += 1; | |
| 2952 | 2928 | } |
| 2953 | 2929 | } |
| 2954 | 2930 | |
| ... | ... | @@ -4404,10 +4380,8 @@ fn performAllTheWork( |
| 4404 | 4380 | comp.link_task_wait_group.reset(); |
| 4405 | 4381 | defer comp.link_task_wait_group.wait(); |
| 4406 | 4382 | |
| 4407 | comp.link_prog_node.increaseEstimatedTotalItems( | |
| 4408 | comp.link_task_queue.queued_prelink.items.len + // already queued prelink tasks | |
| 4409 | comp.link_task_queue.pending_prelink_tasks, // prelink tasks which will be queued | |
| 4410 | ); | |
| 4383 | // Already-queued prelink tasks | |
| 4384 | comp.link_prog_node.increaseEstimatedTotalItems(comp.link_task_queue.queued_prelink.items.len); | |
| 4411 | 4385 | comp.link_task_queue.start(comp); |
| 4412 | 4386 | |
| 4413 | 4387 | if (comp.emit_docs != null) { |
| ... | ... | @@ -4423,6 +4397,7 @@ fn performAllTheWork( |
| 4423 | 4397 | // compiler-rt due to LLD bugs as well, e.g.: |
| 4424 | 4398 | // |
| 4425 | 4399 | // https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611 |
| 4400 | comp.link_task_queue.startPrelinkItem(); | |
| 4426 | 4401 | comp.link_task_wait_group.spawnManager(buildRt, .{ |
| 4427 | 4402 | comp, |
| 4428 | 4403 | "compiler_rt.zig", |
| ... | ... | @@ -4440,6 +4415,7 @@ fn performAllTheWork( |
| 4440 | 4415 | } |
| 4441 | 4416 | |
| 4442 | 4417 | if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) { |
| 4418 | comp.link_task_queue.startPrelinkItem(); | |
| 4443 | 4419 | comp.link_task_wait_group.spawnManager(buildRt, .{ |
| 4444 | 4420 | comp, |
| 4445 | 4421 | "compiler_rt.zig", |
| ... | ... | @@ -4458,6 +4434,7 @@ fn performAllTheWork( |
| 4458 | 4434 | |
| 4459 | 4435 | // hack for stage2_x86_64 + coff |
| 4460 | 4436 | if (comp.queued_jobs.compiler_rt_dyn_lib and comp.compiler_rt_dyn_lib == null) { |
| 4437 | comp.link_task_queue.startPrelinkItem(); | |
| 4461 | 4438 | comp.link_task_wait_group.spawnManager(buildRt, .{ |
| 4462 | 4439 | comp, |
| 4463 | 4440 | "compiler_rt.zig", |
| ... | ... | @@ -4475,6 +4452,7 @@ fn performAllTheWork( |
| 4475 | 4452 | } |
| 4476 | 4453 | |
| 4477 | 4454 | if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) { |
| 4455 | comp.link_task_queue.startPrelinkItem(); | |
| 4478 | 4456 | comp.link_task_wait_group.spawnManager(buildRt, .{ |
| 4479 | 4457 | comp, |
| 4480 | 4458 | "fuzzer.zig", |
| ... | ... | @@ -4489,6 +4467,7 @@ fn performAllTheWork( |
| 4489 | 4467 | } |
| 4490 | 4468 | |
| 4491 | 4469 | if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) { |
| 4470 | comp.link_task_queue.startPrelinkItem(); | |
| 4492 | 4471 | comp.link_task_wait_group.spawnManager(buildRt, .{ |
| 4493 | 4472 | comp, |
| 4494 | 4473 | "ubsan_rt.zig", |
| ... | ... | @@ -4505,6 +4484,7 @@ fn performAllTheWork( |
| 4505 | 4484 | } |
| 4506 | 4485 | |
| 4507 | 4486 | if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) { |
| 4487 | comp.link_task_queue.startPrelinkItem(); | |
| 4508 | 4488 | comp.link_task_wait_group.spawnManager(buildRt, .{ |
| 4509 | 4489 | comp, |
| 4510 | 4490 | "ubsan_rt.zig", |
| ... | ... | @@ -4521,40 +4501,49 @@ fn performAllTheWork( |
| 4521 | 4501 | } |
| 4522 | 4502 | |
| 4523 | 4503 | if (comp.queued_jobs.glibc_shared_objects) { |
| 4504 | comp.link_task_queue.startPrelinkItem(); | |
| 4524 | 4505 | comp.link_task_wait_group.spawnManager(buildGlibcSharedObjects, .{ comp, main_progress_node }); |
| 4525 | 4506 | } |
| 4526 | 4507 | |
| 4527 | 4508 | if (comp.queued_jobs.freebsd_shared_objects) { |
| 4509 | comp.link_task_queue.startPrelinkItem(); | |
| 4528 | 4510 | comp.link_task_wait_group.spawnManager(buildFreeBSDSharedObjects, .{ comp, main_progress_node }); |
| 4529 | 4511 | } |
| 4530 | 4512 | |
| 4531 | 4513 | if (comp.queued_jobs.netbsd_shared_objects) { |
| 4514 | comp.link_task_queue.startPrelinkItem(); | |
| 4532 | 4515 | comp.link_task_wait_group.spawnManager(buildNetBSDSharedObjects, .{ comp, main_progress_node }); |
| 4533 | 4516 | } |
| 4534 | 4517 | |
| 4535 | 4518 | if (comp.queued_jobs.libunwind) { |
| 4519 | comp.link_task_queue.startPrelinkItem(); | |
| 4536 | 4520 | comp.link_task_wait_group.spawnManager(buildLibUnwind, .{ comp, main_progress_node }); |
| 4537 | 4521 | } |
| 4538 | 4522 | |
| 4539 | 4523 | if (comp.queued_jobs.libcxx) { |
| 4524 | comp.link_task_queue.startPrelinkItem(); | |
| 4540 | 4525 | comp.link_task_wait_group.spawnManager(buildLibCxx, .{ comp, main_progress_node }); |
| 4541 | 4526 | } |
| 4542 | 4527 | |
| 4543 | 4528 | if (comp.queued_jobs.libcxxabi) { |
| 4529 | comp.link_task_queue.startPrelinkItem(); | |
| 4544 | 4530 | comp.link_task_wait_group.spawnManager(buildLibCxxAbi, .{ comp, main_progress_node }); |
| 4545 | 4531 | } |
| 4546 | 4532 | |
| 4547 | 4533 | if (comp.queued_jobs.libtsan) { |
| 4534 | comp.link_task_queue.startPrelinkItem(); | |
| 4548 | 4535 | comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node }); |
| 4549 | 4536 | } |
| 4550 | 4537 | |
| 4551 | 4538 | if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) { |
| 4539 | comp.link_task_queue.startPrelinkItem(); | |
| 4552 | 4540 | comp.link_task_wait_group.spawnManager(buildLibZigC, .{ comp, main_progress_node }); |
| 4553 | 4541 | } |
| 4554 | 4542 | |
| 4555 | 4543 | for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| { |
| 4556 | 4544 | if (comp.queued_jobs.musl_crt_file[i]) { |
| 4557 | 4545 | const tag: musl.CrtFile = @enumFromInt(i); |
| 4546 | comp.link_task_queue.startPrelinkItem(); | |
| 4558 | 4547 | comp.link_task_wait_group.spawnManager(buildMuslCrtFile, .{ comp, tag, main_progress_node }); |
| 4559 | 4548 | } |
| 4560 | 4549 | } |
| ... | ... | @@ -4562,6 +4551,7 @@ fn performAllTheWork( |
| 4562 | 4551 | for (0..@typeInfo(glibc.CrtFile).@"enum".fields.len) |i| { |
| 4563 | 4552 | if (comp.queued_jobs.glibc_crt_file[i]) { |
| 4564 | 4553 | const tag: glibc.CrtFile = @enumFromInt(i); |
| 4554 | comp.link_task_queue.startPrelinkItem(); | |
| 4565 | 4555 | comp.link_task_wait_group.spawnManager(buildGlibcCrtFile, .{ comp, tag, main_progress_node }); |
| 4566 | 4556 | } |
| 4567 | 4557 | } |
| ... | ... | @@ -4569,6 +4559,7 @@ fn performAllTheWork( |
| 4569 | 4559 | for (0..@typeInfo(freebsd.CrtFile).@"enum".fields.len) |i| { |
| 4570 | 4560 | if (comp.queued_jobs.freebsd_crt_file[i]) { |
| 4571 | 4561 | const tag: freebsd.CrtFile = @enumFromInt(i); |
| 4562 | comp.link_task_queue.startPrelinkItem(); | |
| 4572 | 4563 | comp.link_task_wait_group.spawnManager(buildFreeBSDCrtFile, .{ comp, tag, main_progress_node }); |
| 4573 | 4564 | } |
| 4574 | 4565 | } |
| ... | ... | @@ -4576,6 +4567,7 @@ fn performAllTheWork( |
| 4576 | 4567 | for (0..@typeInfo(netbsd.CrtFile).@"enum".fields.len) |i| { |
| 4577 | 4568 | if (comp.queued_jobs.netbsd_crt_file[i]) { |
| 4578 | 4569 | const tag: netbsd.CrtFile = @enumFromInt(i); |
| 4570 | comp.link_task_queue.startPrelinkItem(); | |
| 4579 | 4571 | comp.link_task_wait_group.spawnManager(buildNetBSDCrtFile, .{ comp, tag, main_progress_node }); |
| 4580 | 4572 | } |
| 4581 | 4573 | } |
| ... | ... | @@ -4583,6 +4575,7 @@ fn performAllTheWork( |
| 4583 | 4575 | for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| { |
| 4584 | 4576 | if (comp.queued_jobs.wasi_libc_crt_file[i]) { |
| 4585 | 4577 | const tag: wasi_libc.CrtFile = @enumFromInt(i); |
| 4578 | comp.link_task_queue.startPrelinkItem(); | |
| 4586 | 4579 | comp.link_task_wait_group.spawnManager(buildWasiLibcCrtFile, .{ comp, tag, main_progress_node }); |
| 4587 | 4580 | } |
| 4588 | 4581 | } |
| ... | ... | @@ -4590,6 +4583,7 @@ fn performAllTheWork( |
| 4590 | 4583 | for (0..@typeInfo(mingw.CrtFile).@"enum".fields.len) |i| { |
| 4591 | 4584 | if (comp.queued_jobs.mingw_crt_file[i]) { |
| 4592 | 4585 | const tag: mingw.CrtFile = @enumFromInt(i); |
| 4586 | comp.link_task_queue.startPrelinkItem(); | |
| 4593 | 4587 | comp.link_task_wait_group.spawnManager(buildMingwCrtFile, .{ comp, tag, main_progress_node }); |
| 4594 | 4588 | } |
| 4595 | 4589 | } |
| ... | ... | @@ -4661,12 +4655,14 @@ fn performAllTheWork( |
| 4661 | 4655 | } |
| 4662 | 4656 | |
| 4663 | 4657 | while (comp.c_object_work_queue.readItem()) |c_object| { |
| 4658 | comp.link_task_queue.startPrelinkItem(); | |
| 4664 | 4659 | comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateCObject, .{ |
| 4665 | 4660 | comp, c_object, main_progress_node, |
| 4666 | 4661 | }); |
| 4667 | 4662 | } |
| 4668 | 4663 | |
| 4669 | 4664 | while (comp.win32_resource_work_queue.readItem()) |win32_resource| { |
| 4665 | comp.link_task_queue.startPrelinkItem(); | |
| 4670 | 4666 | comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateWin32Resource, .{ |
| 4671 | 4667 | comp, win32_resource, main_progress_node, |
| 4672 | 4668 | }); |
| ... | ... | @@ -4745,7 +4741,7 @@ fn performAllTheWork( |
| 4745 | 4741 | try zcu.flushRetryableFailures(); |
| 4746 | 4742 | |
| 4747 | 4743 | // It's analysis time! Queue up our initial analysis. |
| 4748 | for (zcu.analysis_roots.slice()) |mod| { | |
| 4744 | for (zcu.analysisRoots()) |mod| { | |
| 4749 | 4745 | try comp.queueJob(.{ .analyze_mod = mod }); |
| 4750 | 4746 | } |
| 4751 | 4747 | |
| ... | ... | @@ -4769,15 +4765,14 @@ fn performAllTheWork( |
| 4769 | 4765 | } |
| 4770 | 4766 | }; |
| 4771 | 4767 | |
| 4768 | // We aren't going to queue any more prelink tasks. | |
| 4769 | comp.link_task_queue.finishPrelinkItem(comp); | |
| 4770 | ||
| 4772 | 4771 | if (!comp.separateCodegenThreadOk()) { |
| 4773 | 4772 | // Waits until all input files have been parsed. |
| 4774 | 4773 | comp.link_task_wait_group.wait(); |
| 4775 | 4774 | comp.link_task_wait_group.reset(); |
| 4776 | 4775 | std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{}); |
| 4777 | if (comp.link_task_queue.pending_prelink_tasks > 0) { | |
| 4778 | // Indicates an error occurred preventing prelink phase from completing. | |
| 4779 | return; | |
| 4780 | } | |
| 4781 | 4776 | } |
| 4782 | 4777 | |
| 4783 | 4778 | if (comp.zcu != null) { |
| ... | ... | @@ -5564,6 +5559,7 @@ fn workerUpdateCObject( |
| 5564 | 5559 | c_object: *CObject, |
| 5565 | 5560 | progress_node: std.Progress.Node, |
| 5566 | 5561 | ) void { |
| 5562 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5567 | 5563 | comp.updateCObject(c_object, progress_node) catch |err| switch (err) { |
| 5568 | 5564 | error.AnalysisFail => return, |
| 5569 | 5565 | else => { |
| ... | ... | @@ -5581,6 +5577,7 @@ fn workerUpdateWin32Resource( |
| 5581 | 5577 | win32_resource: *Win32Resource, |
| 5582 | 5578 | progress_node: std.Progress.Node, |
| 5583 | 5579 | ) void { |
| 5580 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5584 | 5581 | comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) { |
| 5585 | 5582 | error.AnalysisFail => return, |
| 5586 | 5583 | else => { |
| ... | ... | @@ -5624,6 +5621,7 @@ fn buildRt( |
| 5624 | 5621 | options: RtOptions, |
| 5625 | 5622 | out: *?CrtFile, |
| 5626 | 5623 | ) void { |
| 5624 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5627 | 5625 | comp.buildOutputFromZig( |
| 5628 | 5626 | root_source_name, |
| 5629 | 5627 | root_name, |
| ... | ... | @@ -5642,6 +5640,7 @@ fn buildRt( |
| 5642 | 5640 | } |
| 5643 | 5641 | |
| 5644 | 5642 | fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.Progress.Node) void { |
| 5643 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5645 | 5644 | if (musl.buildCrtFile(comp, crt_file, prog_node)) |_| { |
| 5646 | 5645 | comp.queued_jobs.musl_crt_file[@intFromEnum(crt_file)] = false; |
| 5647 | 5646 | } else |err| switch (err) { |
| ... | ... | @@ -5653,6 +5652,7 @@ fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.P |
| 5653 | 5652 | } |
| 5654 | 5653 | |
| 5655 | 5654 | fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std.Progress.Node) void { |
| 5655 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5656 | 5656 | if (glibc.buildCrtFile(comp, crt_file, prog_node)) |_| { |
| 5657 | 5657 | comp.queued_jobs.glibc_crt_file[@intFromEnum(crt_file)] = false; |
| 5658 | 5658 | } else |err| switch (err) { |
| ... | ... | @@ -5664,6 +5664,7 @@ fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std |
| 5664 | 5664 | } |
| 5665 | 5665 | |
| 5666 | 5666 | fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5667 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5667 | 5668 | if (glibc.buildSharedObjects(comp, prog_node)) |_| { |
| 5668 | 5669 | // The job should no longer be queued up since it succeeded. |
| 5669 | 5670 | comp.queued_jobs.glibc_shared_objects = false; |
| ... | ... | @@ -5676,6 +5677,7 @@ fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) voi |
| 5676 | 5677 | } |
| 5677 | 5678 | |
| 5678 | 5679 | fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: std.Progress.Node) void { |
| 5680 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5679 | 5681 | if (freebsd.buildCrtFile(comp, crt_file, prog_node)) |_| { |
| 5680 | 5682 | comp.queued_jobs.freebsd_crt_file[@intFromEnum(crt_file)] = false; |
| 5681 | 5683 | } else |err| switch (err) { |
| ... | ... | @@ -5687,6 +5689,7 @@ fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: |
| 5687 | 5689 | } |
| 5688 | 5690 | |
| 5689 | 5691 | fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5692 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5690 | 5693 | if (freebsd.buildSharedObjects(comp, prog_node)) |_| { |
| 5691 | 5694 | // The job should no longer be queued up since it succeeded. |
| 5692 | 5695 | comp.queued_jobs.freebsd_shared_objects = false; |
| ... | ... | @@ -5699,6 +5702,7 @@ fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) v |
| 5699 | 5702 | } |
| 5700 | 5703 | |
| 5701 | 5704 | fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: std.Progress.Node) void { |
| 5705 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5702 | 5706 | if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| { |
| 5703 | 5707 | comp.queued_jobs.netbsd_crt_file[@intFromEnum(crt_file)] = false; |
| 5704 | 5708 | } else |err| switch (err) { |
| ... | ... | @@ -5710,6 +5714,7 @@ fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: s |
| 5710 | 5714 | } |
| 5711 | 5715 | |
| 5712 | 5716 | fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5717 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5713 | 5718 | if (netbsd.buildSharedObjects(comp, prog_node)) |_| { |
| 5714 | 5719 | // The job should no longer be queued up since it succeeded. |
| 5715 | 5720 | comp.queued_jobs.netbsd_shared_objects = false; |
| ... | ... | @@ -5722,6 +5727,7 @@ fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) vo |
| 5722 | 5727 | } |
| 5723 | 5728 | |
| 5724 | 5729 | fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void { |
| 5730 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5725 | 5731 | if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| { |
| 5726 | 5732 | comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false; |
| 5727 | 5733 | } else |err| switch (err) { |
| ... | ... | @@ -5733,6 +5739,7 @@ fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std |
| 5733 | 5739 | } |
| 5734 | 5740 | |
| 5735 | 5741 | fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void { |
| 5742 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5736 | 5743 | if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| { |
| 5737 | 5744 | comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false; |
| 5738 | 5745 | } else |err| switch (err) { |
| ... | ... | @@ -5744,6 +5751,7 @@ fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_no |
| 5744 | 5751 | } |
| 5745 | 5752 | |
| 5746 | 5753 | fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5754 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5747 | 5755 | if (libunwind.buildStaticLib(comp, prog_node)) |_| { |
| 5748 | 5756 | comp.queued_jobs.libunwind = false; |
| 5749 | 5757 | } else |err| switch (err) { |
| ... | ... | @@ -5753,6 +5761,7 @@ fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5753 | 5761 | } |
| 5754 | 5762 | |
| 5755 | 5763 | fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5764 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5756 | 5765 | if (libcxx.buildLibCxx(comp, prog_node)) |_| { |
| 5757 | 5766 | comp.queued_jobs.libcxx = false; |
| 5758 | 5767 | } else |err| switch (err) { |
| ... | ... | @@ -5762,6 +5771,7 @@ fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5762 | 5771 | } |
| 5763 | 5772 | |
| 5764 | 5773 | fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5774 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5765 | 5775 | if (libcxx.buildLibCxxAbi(comp, prog_node)) |_| { |
| 5766 | 5776 | comp.queued_jobs.libcxxabi = false; |
| 5767 | 5777 | } else |err| switch (err) { |
| ... | ... | @@ -5771,6 +5781,7 @@ fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5771 | 5781 | } |
| 5772 | 5782 | |
| 5773 | 5783 | fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5784 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5774 | 5785 | if (libtsan.buildTsan(comp, prog_node)) |_| { |
| 5775 | 5786 | comp.queued_jobs.libtsan = false; |
| 5776 | 5787 | } else |err| switch (err) { |
| ... | ... | @@ -5780,6 +5791,7 @@ fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5780 | 5791 | } |
| 5781 | 5792 | |
| 5782 | 5793 | fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void { |
| 5794 | defer comp.link_task_queue.finishPrelinkItem(comp); | |
| 5783 | 5795 | comp.buildOutputFromZig( |
| 5784 | 5796 | "c.zig", |
| 5785 | 5797 | "zigc", |
| ... | ... | @@ -7717,6 +7729,7 @@ pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const |
| 7717 | 7729 | /// Only valid to call during `update`. Automatically handles queuing up a |
| 7718 | 7730 | /// linker worker task if there is not already one. |
| 7719 | 7731 | pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) void { |
| 7732 | comp.link_prog_node.increaseEstimatedTotalItems(tasks.len); | |
| 7720 | 7733 | comp.link_task_queue.enqueuePrelink(comp, tasks) catch |err| switch (err) { |
| 7721 | 7734 | error.OutOfMemory => return comp.setAllocFailure(), |
| 7722 | 7735 | }; |
| ... | ... | @@ -7789,6 +7802,27 @@ fn getCrtPathsInner( |
| 7789 | 7802 | }; |
| 7790 | 7803 | } |
| 7791 | 7804 | |
| 7805 | pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { | |
| 7806 | // Avoid deadlocking on building import libs such as kernel32.lib | |
| 7807 | // This can happen when the user uses `build-exe foo.obj -lkernel32` and | |
| 7808 | // then when we create a sub-Compilation for zig libc, it also tries to | |
| 7809 | // build kernel32.lib. | |
| 7810 | if (comp.skip_linker_dependencies) return; | |
| 7811 | const target = &comp.root_mod.resolved_target.result; | |
| 7812 | if (target.os.tag != .windows or target.ofmt == .c) return; | |
| 7813 | ||
| 7814 | // This happens when an `extern "foo"` function is referenced. | |
| 7815 | // If we haven't seen this library yet and we're targeting Windows, we need | |
| 7816 | // to queue up a work item to produce the DLL import library for this. | |
| 7817 | const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name); | |
| 7818 | if (gop.found_existing) return; | |
| 7819 | { | |
| 7820 | errdefer _ = comp.windows_libs.pop(); | |
| 7821 | gop.key_ptr.* = try comp.gpa.dupe(u8, lib_name); | |
| 7822 | } | |
| 7823 | try comp.queueJob(.{ .windows_import_lib = gop.index }); | |
| 7824 | } | |
| 7825 | ||
| 7792 | 7826 | /// This decides the optimization mode for all zig-provided libraries, including |
| 7793 | 7827 | /// compiler-rt, libcxx, libc, libunwind, etc. |
| 7794 | 7828 | pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { |
src/InternPool.zig+22-16| ... | ... | @@ -1137,13 +1137,16 @@ const Local = struct { |
| 1137 | 1137 | const elem_info = @typeInfo(Elem).@"struct"; |
| 1138 | 1138 | const elem_fields = elem_info.fields; |
| 1139 | 1139 | var new_fields: [elem_fields.len]std.builtin.Type.StructField = undefined; |
| 1140 | for (&new_fields, elem_fields) |*new_field, elem_field| new_field.* = .{ | |
| 1141 | .name = elem_field.name, | |
| 1142 | .type = *[len]elem_field.type, | |
| 1143 | .default_value_ptr = null, | |
| 1144 | .is_comptime = false, | |
| 1145 | .alignment = 0, | |
| 1146 | }; | |
| 1140 | for (&new_fields, elem_fields) |*new_field, elem_field| { | |
| 1141 | const T = *[len]elem_field.type; | |
| 1142 | new_field.* = .{ | |
| 1143 | .name = elem_field.name, | |
| 1144 | .type = T, | |
| 1145 | .default_value_ptr = null, | |
| 1146 | .is_comptime = false, | |
| 1147 | .alignment = @alignOf(T), | |
| 1148 | }; | |
| 1149 | } | |
| 1147 | 1150 | return @Type(.{ .@"struct" = .{ |
| 1148 | 1151 | .layout = .auto, |
| 1149 | 1152 | .fields = &new_fields, |
| ... | ... | @@ -1158,22 +1161,25 @@ const Local = struct { |
| 1158 | 1161 | const elem_info = @typeInfo(Elem).@"struct"; |
| 1159 | 1162 | const elem_fields = elem_info.fields; |
| 1160 | 1163 | var new_fields: [elem_fields.len]std.builtin.Type.StructField = undefined; |
| 1161 | for (&new_fields, elem_fields) |*new_field, elem_field| new_field.* = .{ | |
| 1162 | .name = elem_field.name, | |
| 1163 | .type = @Type(.{ .pointer = .{ | |
| 1164 | for (&new_fields, elem_fields) |*new_field, elem_field| { | |
| 1165 | const T = @Type(.{ .pointer = .{ | |
| 1164 | 1166 | .size = opts.size, |
| 1165 | 1167 | .is_const = opts.is_const, |
| 1166 | 1168 | .is_volatile = false, |
| 1167 | .alignment = 0, | |
| 1169 | .alignment = @alignOf(elem_field.type), | |
| 1168 | 1170 | .address_space = .generic, |
| 1169 | 1171 | .child = elem_field.type, |
| 1170 | 1172 | .is_allowzero = false, |
| 1171 | 1173 | .sentinel_ptr = null, |
| 1172 | } }), | |
| 1173 | .default_value_ptr = null, | |
| 1174 | .is_comptime = false, | |
| 1175 | .alignment = 0, | |
| 1176 | }; | |
| 1174 | } }); | |
| 1175 | new_field.* = .{ | |
| 1176 | .name = elem_field.name, | |
| 1177 | .type = T, | |
| 1178 | .default_value_ptr = null, | |
| 1179 | .is_comptime = false, | |
| 1180 | .alignment = @alignOf(T), | |
| 1181 | }; | |
| 1182 | } | |
| 1177 | 1183 | return @Type(.{ .@"struct" = .{ |
| 1178 | 1184 | .layout = .auto, |
| 1179 | 1185 | .fields = &new_fields, |
src/Package/Fetch.zig+148-193| ... | ... | @@ -385,21 +385,23 @@ pub fn run(f: *Fetch) RunError!void { |
| 385 | 385 | var resource: Resource = .{ .dir = dir }; |
| 386 | 386 | return f.runResource(path_or_url, &resource, null); |
| 387 | 387 | } else |dir_err| { |
| 388 | var server_header_buffer: [init_resource_buffer_size]u8 = undefined; | |
| 389 | ||
| 388 | 390 | const file_err = if (dir_err == error.NotDir) e: { |
| 389 | 391 | if (fs.cwd().openFile(path_or_url, .{})) |file| { |
| 390 | var resource: Resource = .{ .file = file }; | |
| 392 | var resource: Resource = .{ .file = file.reader(&server_header_buffer) }; | |
| 391 | 393 | return f.runResource(path_or_url, &resource, null); |
| 392 | 394 | } else |err| break :e err; |
| 393 | 395 | } else dir_err; |
| 394 | 396 | |
| 395 | 397 | const uri = std.Uri.parse(path_or_url) catch |uri_err| { |
| 396 | 398 | return f.fail(0, try eb.printString( |
| 397 | "'{s}' could not be recognized as a file path ({s}) or an URL ({s})", | |
| 398 | .{ path_or_url, @errorName(file_err), @errorName(uri_err) }, | |
| 399 | "'{s}' could not be recognized as a file path ({t}) or an URL ({t})", | |
| 400 | .{ path_or_url, file_err, uri_err }, | |
| 399 | 401 | )); |
| 400 | 402 | }; |
| 401 | var server_header_buffer: [header_buffer_size]u8 = undefined; | |
| 402 | var resource = try f.initResource(uri, &server_header_buffer); | |
| 403 | var resource: Resource = undefined; | |
| 404 | try f.initResource(uri, &resource, &server_header_buffer); | |
| 403 | 405 | return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, null); |
| 404 | 406 | } |
| 405 | 407 | }, |
| ... | ... | @@ -464,8 +466,9 @@ pub fn run(f: *Fetch) RunError!void { |
| 464 | 466 | f.location_tok, |
| 465 | 467 | try eb.printString("invalid URI: {s}", .{@errorName(err)}), |
| 466 | 468 | ); |
| 467 | var server_header_buffer: [header_buffer_size]u8 = undefined; | |
| 468 | var resource = try f.initResource(uri, &server_header_buffer); | |
| 469 | var buffer: [init_resource_buffer_size]u8 = undefined; | |
| 470 | var resource: Resource = undefined; | |
| 471 | try f.initResource(uri, &resource, &buffer); | |
| 469 | 472 | return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, remote.hash); |
| 470 | 473 | } |
| 471 | 474 | |
| ... | ... | @@ -866,8 +869,8 @@ fn fail(f: *Fetch, msg_tok: std.zig.Ast.TokenIndex, msg_str: u32) RunError { |
| 866 | 869 | } |
| 867 | 870 | |
| 868 | 871 | const Resource = union(enum) { |
| 869 | file: fs.File, | |
| 870 | http_request: std.http.Client.Request, | |
| 872 | file: fs.File.Reader, | |
| 873 | http_request: HttpRequest, | |
| 871 | 874 | git: Git, |
| 872 | 875 | dir: fs.Dir, |
| 873 | 876 | |
| ... | ... | @@ -877,10 +880,16 @@ const Resource = union(enum) { |
| 877 | 880 | want_oid: git.Oid, |
| 878 | 881 | }; |
| 879 | 882 | |
| 883 | const HttpRequest = struct { | |
| 884 | request: std.http.Client.Request, | |
| 885 | response: std.http.Client.Response, | |
| 886 | buffer: []u8, | |
| 887 | }; | |
| 888 | ||
| 880 | 889 | fn deinit(resource: *Resource) void { |
| 881 | 890 | switch (resource.*) { |
| 882 | .file => |*file| file.close(), | |
| 883 | .http_request => |*req| req.deinit(), | |
| 891 | .file => |*file_reader| file_reader.file.close(), | |
| 892 | .http_request => |*http_request| http_request.request.deinit(), | |
| 884 | 893 | .git => |*git_resource| { |
| 885 | 894 | git_resource.fetch_stream.deinit(); |
| 886 | 895 | git_resource.session.deinit(); |
| ... | ... | @@ -890,21 +899,13 @@ const Resource = union(enum) { |
| 890 | 899 | resource.* = undefined; |
| 891 | 900 | } |
| 892 | 901 | |
| 893 | fn reader(resource: *Resource) std.io.AnyReader { | |
| 894 | return .{ | |
| 895 | .context = resource, | |
| 896 | .readFn = read, | |
| 897 | }; | |
| 898 | } | |
| 899 | ||
| 900 | fn read(context: *const anyopaque, buffer: []u8) anyerror!usize { | |
| 901 | const resource: *Resource = @constCast(@ptrCast(@alignCast(context))); | |
| 902 | switch (resource.*) { | |
| 903 | .file => |*f| return f.read(buffer), | |
| 904 | .http_request => |*r| return r.read(buffer), | |
| 905 | .git => |*g| return g.fetch_stream.read(buffer), | |
| 902 | fn reader(resource: *Resource) *std.Io.Reader { | |
| 903 | return switch (resource.*) { | |
| 904 | .file => |*file_reader| return &file_reader.interface, | |
| 905 | .http_request => |*http_request| return http_request.response.reader(http_request.buffer), | |
| 906 | .git => |*g| return &g.fetch_stream.reader, | |
| 906 | 907 | .dir => unreachable, |
| 907 | } | |
| 908 | }; | |
| 908 | 909 | } |
| 909 | 910 | }; |
| 910 | 911 | |
| ... | ... | @@ -967,20 +968,22 @@ const FileType = enum { |
| 967 | 968 | } |
| 968 | 969 | }; |
| 969 | 970 | |
| 970 | const header_buffer_size = 16 * 1024; | |
| 971 | const init_resource_buffer_size = git.Packet.max_data_length; | |
| 971 | 972 | |
| 972 | fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Resource { | |
| 973 | fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u8) RunError!void { | |
| 973 | 974 | const gpa = f.arena.child_allocator; |
| 974 | 975 | const arena = f.arena.allocator(); |
| 975 | 976 | const eb = &f.error_bundle; |
| 976 | 977 | |
| 977 | 978 | if (ascii.eqlIgnoreCase(uri.scheme, "file")) { |
| 978 | 979 | const path = try uri.path.toRawMaybeAlloc(arena); |
| 979 | return .{ .file = f.parent_package_root.openFile(path, .{}) catch |err| { | |
| 980 | return f.fail(f.location_tok, try eb.printString("unable to open '{f}{s}': {s}", .{ | |
| 981 | f.parent_package_root, path, @errorName(err), | |
| 980 | const file = f.parent_package_root.openFile(path, .{}) catch |err| { | |
| 981 | return f.fail(f.location_tok, try eb.printString("unable to open '{f}{s}': {t}", .{ | |
| 982 | f.parent_package_root, path, err, | |
| 982 | 983 | })); |
| 983 | } }; | |
| 984 | }; | |
| 985 | resource.* = .{ .file = file.reader(reader_buffer) }; | |
| 986 | return; | |
| 984 | 987 | } |
| 985 | 988 | |
| 986 | 989 | const http_client = f.job_queue.http_client; |
| ... | ... | @@ -988,37 +991,35 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re |
| 988 | 991 | if (ascii.eqlIgnoreCase(uri.scheme, "http") or |
| 989 | 992 | ascii.eqlIgnoreCase(uri.scheme, "https")) |
| 990 | 993 | { |
| 991 | var req = http_client.open(.GET, uri, .{ | |
| 992 | .server_header_buffer = server_header_buffer, | |
| 993 | }) catch |err| { | |
| 994 | return f.fail(f.location_tok, try eb.printString( | |
| 995 | "unable to connect to server: {s}", | |
| 996 | .{@errorName(err)}, | |
| 997 | )); | |
| 998 | }; | |
| 999 | errdefer req.deinit(); // releases more than memory | |
| 1000 | ||
| 1001 | req.send() catch |err| { | |
| 1002 | return f.fail(f.location_tok, try eb.printString( | |
| 1003 | "HTTP request failed: {s}", | |
| 1004 | .{@errorName(err)}, | |
| 1005 | )); | |
| 1006 | }; | |
| 1007 | req.wait() catch |err| { | |
| 1008 | return f.fail(f.location_tok, try eb.printString( | |
| 1009 | "invalid HTTP response: {s}", | |
| 1010 | .{@errorName(err)}, | |
| 1011 | )); | |
| 994 | resource.* = .{ .http_request = .{ | |
| 995 | .request = http_client.request(.GET, uri, .{}) catch |err| | |
| 996 | return f.fail(f.location_tok, try eb.printString("unable to connect to server: {t}", .{err})), | |
| 997 | .response = undefined, | |
| 998 | .buffer = reader_buffer, | |
| 999 | } }; | |
| 1000 | const request = &resource.http_request.request; | |
| 1001 | errdefer request.deinit(); | |
| 1002 | ||
| 1003 | request.sendBodiless() catch |err| | |
| 1004 | return f.fail(f.location_tok, try eb.printString("HTTP request failed: {t}", .{err})); | |
| 1005 | ||
| 1006 | var redirect_buffer: [1024]u8 = undefined; | |
| 1007 | const response = &resource.http_request.response; | |
| 1008 | response.* = request.receiveHead(&redirect_buffer) catch |err| switch (err) { | |
| 1009 | error.ReadFailed => { | |
| 1010 | return f.fail(f.location_tok, try eb.printString("HTTP response read failure: {t}", .{ | |
| 1011 | request.connection.?.getReadError().?, | |
| 1012 | })); | |
| 1013 | }, | |
| 1014 | else => |e| return f.fail(f.location_tok, try eb.printString("invalid HTTP response: {t}", .{e})), | |
| 1012 | 1015 | }; |
| 1013 | 1016 | |
| 1014 | if (req.response.status != .ok) { | |
| 1015 | return f.fail(f.location_tok, try eb.printString( | |
| 1016 | "bad HTTP response code: '{d} {s}'", | |
| 1017 | .{ @intFromEnum(req.response.status), req.response.status.phrase() orelse "" }, | |
| 1018 | )); | |
| 1019 | } | |
| 1017 | if (response.head.status != .ok) return f.fail(f.location_tok, try eb.printString( | |
| 1018 | "bad HTTP response code: '{d} {s}'", | |
| 1019 | .{ response.head.status, response.head.status.phrase() orelse "" }, | |
| 1020 | )); | |
| 1020 | 1021 | |
| 1021 | return .{ .http_request = req }; | |
| 1022 | return; | |
| 1022 | 1023 | } |
| 1023 | 1024 | |
| 1024 | 1025 | if (ascii.eqlIgnoreCase(uri.scheme, "git+http") or |
| ... | ... | @@ -1026,7 +1027,7 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re |
| 1026 | 1027 | { |
| 1027 | 1028 | var transport_uri = uri; |
| 1028 | 1029 | transport_uri.scheme = uri.scheme["git+".len..]; |
| 1029 | var session = git.Session.init(gpa, http_client, transport_uri, server_header_buffer) catch |err| { | |
| 1030 | var session = git.Session.init(gpa, http_client, transport_uri, reader_buffer) catch |err| { | |
| 1030 | 1031 | return f.fail(f.location_tok, try eb.printString( |
| 1031 | 1032 | "unable to discover remote git server capabilities: {s}", |
| 1032 | 1033 | .{@errorName(err)}, |
| ... | ... | @@ -1042,16 +1043,12 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re |
| 1042 | 1043 | const want_ref_head = try std.fmt.allocPrint(arena, "refs/heads/{s}", .{want_ref}); |
| 1043 | 1044 | const want_ref_tag = try std.fmt.allocPrint(arena, "refs/tags/{s}", .{want_ref}); |
| 1044 | 1045 | |
| 1045 | var ref_iterator = session.listRefs(.{ | |
| 1046 | var ref_iterator: git.Session.RefIterator = undefined; | |
| 1047 | session.listRefs(&ref_iterator, .{ | |
| 1046 | 1048 | .ref_prefixes = &.{ want_ref, want_ref_head, want_ref_tag }, |
| 1047 | 1049 | .include_peeled = true, |
| 1048 | .server_header_buffer = server_header_buffer, | |
| 1049 | }) catch |err| { | |
| 1050 | return f.fail(f.location_tok, try eb.printString( | |
| 1051 | "unable to list refs: {s}", | |
| 1052 | .{@errorName(err)}, | |
| 1053 | )); | |
| 1054 | }; | |
| 1050 | .buffer = reader_buffer, | |
| 1051 | }) catch |err| return f.fail(f.location_tok, try eb.printString("unable to list refs: {t}", .{err})); | |
| 1055 | 1052 | defer ref_iterator.deinit(); |
| 1056 | 1053 | while (ref_iterator.next() catch |err| { |
| 1057 | 1054 | return f.fail(f.location_tok, try eb.printString( |
| ... | ... | @@ -1089,25 +1086,21 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re |
| 1089 | 1086 | |
| 1090 | 1087 | var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined; |
| 1091 | 1088 | _ = std.fmt.bufPrint(&want_oid_buf, "{f}", .{want_oid}) catch unreachable; |
| 1092 | var fetch_stream = session.fetch(&.{&want_oid_buf}, server_header_buffer) catch |err| { | |
| 1093 | return f.fail(f.location_tok, try eb.printString( | |
| 1094 | "unable to create fetch stream: {s}", | |
| 1095 | .{@errorName(err)}, | |
| 1096 | )); | |
| 1089 | var fetch_stream: git.Session.FetchStream = undefined; | |
| 1090 | session.fetch(&fetch_stream, &.{&want_oid_buf}, reader_buffer) catch |err| { | |
| 1091 | return f.fail(f.location_tok, try eb.printString("unable to create fetch stream: {t}", .{err})); | |
| 1097 | 1092 | }; |
| 1098 | 1093 | errdefer fetch_stream.deinit(); |
| 1099 | 1094 | |
| 1100 | return .{ .git = .{ | |
| 1095 | resource.* = .{ .git = .{ | |
| 1101 | 1096 | .session = session, |
| 1102 | 1097 | .fetch_stream = fetch_stream, |
| 1103 | 1098 | .want_oid = want_oid, |
| 1104 | 1099 | } }; |
| 1100 | return; | |
| 1105 | 1101 | } |
| 1106 | 1102 | |
| 1107 | return f.fail(f.location_tok, try eb.printString( | |
| 1108 | "unsupported URL scheme: {s}", | |
| 1109 | .{uri.scheme}, | |
| 1110 | )); | |
| 1103 | return f.fail(f.location_tok, try eb.printString("unsupported URL scheme: {s}", .{uri.scheme})); | |
| 1111 | 1104 | } |
| 1112 | 1105 | |
| 1113 | 1106 | fn unpackResource( |
| ... | ... | @@ -1121,9 +1114,11 @@ fn unpackResource( |
| 1121 | 1114 | .file => FileType.fromPath(uri_path) orelse |
| 1122 | 1115 | return f.fail(f.location_tok, try eb.printString("unknown file type: '{s}'", .{uri_path})), |
| 1123 | 1116 | |
| 1124 | .http_request => |req| ft: { | |
| 1117 | .http_request => |*http_request| ft: { | |
| 1118 | const head = &http_request.response.head; | |
| 1119 | ||
| 1125 | 1120 | // Content-Type takes first precedence. |
| 1126 | const content_type = req.response.content_type orelse | |
| 1121 | const content_type = head.content_type orelse | |
| 1127 | 1122 | return f.fail(f.location_tok, try eb.addString("missing 'Content-Type' header")); |
| 1128 | 1123 | |
| 1129 | 1124 | // Extract the MIME type, ignoring charset and boundary directives |
| ... | ... | @@ -1165,7 +1160,7 @@ fn unpackResource( |
| 1165 | 1160 | } |
| 1166 | 1161 | |
| 1167 | 1162 | // Next, the filename from 'content-disposition: attachment' takes precedence. |
| 1168 | if (req.response.content_disposition) |cd_header| { | |
| 1163 | if (head.content_disposition) |cd_header| { | |
| 1169 | 1164 | break :ft FileType.fromContentDisposition(cd_header) orelse { |
| 1170 | 1165 | return f.fail(f.location_tok, try eb.printString( |
| 1171 | 1166 | "unsupported Content-Disposition header value: '{s}' for Content-Type=application/octet-stream", |
| ... | ... | @@ -1176,10 +1171,7 @@ fn unpackResource( |
| 1176 | 1171 | |
| 1177 | 1172 | // Finally, the path from the URI is used. |
| 1178 | 1173 | break :ft FileType.fromPath(uri_path) orelse { |
| 1179 | return f.fail(f.location_tok, try eb.printString( | |
| 1180 | "unknown file type: '{s}'", | |
| 1181 | .{uri_path}, | |
| 1182 | )); | |
| 1174 | return f.fail(f.location_tok, try eb.printString("unknown file type: '{s}'", .{uri_path})); | |
| 1183 | 1175 | }; |
| 1184 | 1176 | }, |
| 1185 | 1177 | |
| ... | ... | @@ -1187,10 +1179,9 @@ fn unpackResource( |
| 1187 | 1179 | |
| 1188 | 1180 | .dir => |dir| { |
| 1189 | 1181 | f.recursiveDirectoryCopy(dir, tmp_directory.handle) catch |err| { |
| 1190 | return f.fail(f.location_tok, try eb.printString( | |
| 1191 | "unable to copy directory '{s}': {s}", | |
| 1192 | .{ uri_path, @errorName(err) }, | |
| 1193 | )); | |
| 1182 | return f.fail(f.location_tok, try eb.printString("unable to copy directory '{s}': {t}", .{ | |
| 1183 | uri_path, err, | |
| 1184 | })); | |
| 1194 | 1185 | }; |
| 1195 | 1186 | return .{}; |
| 1196 | 1187 | }, |
| ... | ... | @@ -1198,27 +1189,17 @@ fn unpackResource( |
| 1198 | 1189 | |
| 1199 | 1190 | switch (file_type) { |
| 1200 | 1191 | .tar => { |
| 1201 | var adapter_buffer: [1024]u8 = undefined; | |
| 1202 | var adapter = resource.reader().adaptToNewApi(&adapter_buffer); | |
| 1203 | return unpackTarball(f, tmp_directory.handle, &adapter.new_interface); | |
| 1192 | return unpackTarball(f, tmp_directory.handle, resource.reader()); | |
| 1204 | 1193 | }, |
| 1205 | 1194 | .@"tar.gz" => { |
| 1206 | var adapter_buffer: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined; | |
| 1207 | var adapter = resource.reader().adaptToNewApi(&adapter_buffer); | |
| 1208 | 1195 | var flate_buffer: [std.compress.flate.max_window_len]u8 = undefined; |
| 1209 | var decompress: std.compress.flate.Decompress = .init(&adapter.new_interface, .gzip, &flate_buffer); | |
| 1196 | var decompress: std.compress.flate.Decompress = .init(resource.reader(), .gzip, &flate_buffer); | |
| 1210 | 1197 | return try unpackTarball(f, tmp_directory.handle, &decompress.reader); |
| 1211 | 1198 | }, |
| 1212 | 1199 | .@"tar.xz" => { |
| 1213 | 1200 | const gpa = f.arena.child_allocator; |
| 1214 | const reader = resource.reader(); | |
| 1215 | var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader); | |
| 1216 | var dcp = std.compress.xz.decompress(gpa, br.reader()) catch |err| { | |
| 1217 | return f.fail(f.location_tok, try eb.printString( | |
| 1218 | "unable to decompress tarball: {s}", | |
| 1219 | .{@errorName(err)}, | |
| 1220 | )); | |
| 1221 | }; | |
| 1201 | var dcp = std.compress.xz.decompress(gpa, resource.reader().adaptToOldInterface()) catch |err| | |
| 1202 | return f.fail(f.location_tok, try eb.printString("unable to decompress tarball: {t}", .{err})); | |
| 1222 | 1203 | defer dcp.deinit(); |
| 1223 | 1204 | var adapter_buffer: [1024]u8 = undefined; |
| 1224 | 1205 | var adapter = dcp.reader().adaptToNewApi(&adapter_buffer); |
| ... | ... | @@ -1227,9 +1208,7 @@ fn unpackResource( |
| 1227 | 1208 | .@"tar.zst" => { |
| 1228 | 1209 | const window_size = std.compress.zstd.default_window_len; |
| 1229 | 1210 | const window_buffer = try f.arena.allocator().create([window_size]u8); |
| 1230 | var adapter_buffer: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined; | |
| 1231 | var adapter = resource.reader().adaptToNewApi(&adapter_buffer); | |
| 1232 | var decompress: std.compress.zstd.Decompress = .init(&adapter.new_interface, window_buffer, .{ | |
| 1211 | var decompress: std.compress.zstd.Decompress = .init(resource.reader(), window_buffer, .{ | |
| 1233 | 1212 | .verify_checksum = false, |
| 1234 | 1213 | }); |
| 1235 | 1214 | return try unpackTarball(f, tmp_directory.handle, &decompress.reader); |
| ... | ... | @@ -1237,12 +1216,15 @@ fn unpackResource( |
| 1237 | 1216 | .git_pack => return unpackGitPack(f, tmp_directory.handle, &resource.git) catch |err| switch (err) { |
| 1238 | 1217 | error.FetchFailed => return error.FetchFailed, |
| 1239 | 1218 | error.OutOfMemory => return error.OutOfMemory, |
| 1240 | else => |e| return f.fail(f.location_tok, try eb.printString( | |
| 1241 | "unable to unpack git files: {s}", | |
| 1242 | .{@errorName(e)}, | |
| 1219 | else => |e| return f.fail(f.location_tok, try eb.printString("unable to unpack git files: {t}", .{e})), | |
| 1220 | }, | |
| 1221 | .zip => return unzip(f, tmp_directory.handle, resource.reader()) catch |err| switch (err) { | |
| 1222 | error.ReadFailed => return f.fail(f.location_tok, try eb.printString( | |
| 1223 | "failed reading resource: {t}", | |
| 1224 | .{err}, | |
| 1243 | 1225 | )), |
| 1226 | else => |e| return e, | |
| 1244 | 1227 | }, |
| 1245 | .zip => return try unzip(f, tmp_directory.handle, resource.reader()), | |
| 1246 | 1228 | } |
| 1247 | 1229 | } |
| 1248 | 1230 | |
| ... | ... | @@ -1277,99 +1259,69 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: *std.Io.Reader) RunError!Un |
| 1277 | 1259 | return res; |
| 1278 | 1260 | } |
| 1279 | 1261 | |
| 1280 | fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { | |
| 1262 | fn unzip(f: *Fetch, out_dir: fs.Dir, reader: *std.Io.Reader) error{ ReadFailed, OutOfMemory, FetchFailed }!UnpackResult { | |
| 1281 | 1263 | // We write the entire contents to a file first because zip files |
| 1282 | 1264 | // must be processed back to front and they could be too large to |
| 1283 | 1265 | // load into memory. |
| 1284 | 1266 | |
| 1285 | 1267 | const cache_root = f.job_queue.global_cache; |
| 1286 | ||
| 1287 | // TODO: the downside of this solution is if we get a failure/crash/oom/power out | |
| 1288 | // during this process, we leave behind a zip file that would be | |
| 1289 | // difficult to know if/when it can be cleaned up. | |
| 1290 | // Might be worth it to use a mechanism that enables other processes | |
| 1291 | // to see if the owning process of a file is still alive (on linux this | |
| 1292 | // can be done with file locks). | |
| 1293 | // Coupled with this mechansism, we could also use slots (i.e. zig-cache/tmp/0, | |
| 1294 | // zig-cache/tmp/1, etc) which would mean that subsequent runs would | |
| 1295 | // automatically clean up old dead files. | |
| 1296 | // This could all be done with a simple TmpFile abstraction. | |
| 1297 | 1268 | const prefix = "tmp/"; |
| 1298 | 1269 | const suffix = ".zip"; |
| 1299 | ||
| 1300 | const random_bytes_count = 20; | |
| 1301 | const random_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); | |
| 1302 | var zip_path: [prefix.len + random_path_len + suffix.len]u8 = undefined; | |
| 1303 | @memcpy(zip_path[0..prefix.len], prefix); | |
| 1304 | @memcpy(zip_path[prefix.len + random_path_len ..], suffix); | |
| 1305 | { | |
| 1306 | var random_bytes: [random_bytes_count]u8 = undefined; | |
| 1307 | std.crypto.random.bytes(&random_bytes); | |
| 1308 | _ = std.fs.base64_encoder.encode( | |
| 1309 | zip_path[prefix.len..][0..random_path_len], | |
| 1310 | &random_bytes, | |
| 1311 | ); | |
| 1312 | } | |
| 1313 | ||
| 1314 | defer cache_root.handle.deleteFile(&zip_path) catch {}; | |
| 1315 | ||
| 1316 | 1270 | const eb = &f.error_bundle; |
| 1317 | ||
| 1318 | { | |
| 1319 | var zip_file = cache_root.handle.createFile( | |
| 1320 | &zip_path, | |
| 1321 | .{}, | |
| 1322 | ) catch |err| return f.fail(f.location_tok, try eb.printString( | |
| 1323 | "failed to create tmp zip file: {s}", | |
| 1324 | .{@errorName(err)}, | |
| 1325 | )); | |
| 1326 | defer zip_file.close(); | |
| 1327 | var buf: [4096]u8 = undefined; | |
| 1328 | while (true) { | |
| 1329 | const len = reader.readAll(&buf) catch |err| return f.fail(f.location_tok, try eb.printString( | |
| 1330 | "read zip stream failed: {s}", | |
| 1331 | .{@errorName(err)}, | |
| 1332 | )); | |
| 1333 | if (len == 0) break; | |
| 1334 | zip_file.deprecatedWriter().writeAll(buf[0..len]) catch |err| return f.fail(f.location_tok, try eb.printString( | |
| 1335 | "write temporary zip file failed: {s}", | |
| 1336 | .{@errorName(err)}, | |
| 1337 | )); | |
| 1338 | } | |
| 1339 | } | |
| 1271 | const random_len = @sizeOf(u64) * 2; | |
| 1272 | ||
| 1273 | var zip_path: [prefix.len + random_len + suffix.len]u8 = undefined; | |
| 1274 | zip_path[0..prefix.len].* = prefix.*; | |
| 1275 | zip_path[prefix.len + random_len ..].* = suffix.*; | |
| 1276 | ||
| 1277 | var zip_file = while (true) { | |
| 1278 | const random_integer = std.crypto.random.int(u64); | |
| 1279 | zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer); | |
| 1280 | ||
| 1281 | break cache_root.handle.createFile(&zip_path, .{ | |
| 1282 | .exclusive = true, | |
| 1283 | .read = true, | |
| 1284 | }) catch |err| switch (err) { | |
| 1285 | error.PathAlreadyExists => continue, | |
| 1286 | else => |e| return f.fail( | |
| 1287 | f.location_tok, | |
| 1288 | try eb.printString("failed to create temporary zip file: {t}", .{e}), | |
| 1289 | ), | |
| 1290 | }; | |
| 1291 | }; | |
| 1292 | defer zip_file.close(); | |
| 1293 | var zip_file_buffer: [4096]u8 = undefined; | |
| 1294 | var zip_file_reader = b: { | |
| 1295 | var zip_file_writer = zip_file.writer(&zip_file_buffer); | |
| 1296 | ||
| 1297 | _ = reader.streamRemaining(&zip_file_writer.interface) catch |err| switch (err) { | |
| 1298 | error.ReadFailed => return error.ReadFailed, | |
| 1299 | error.WriteFailed => return f.fail( | |
| 1300 | f.location_tok, | |
| 1301 | try eb.printString("failed writing temporary zip file: {t}", .{err}), | |
| 1302 | ), | |
| 1303 | }; | |
| 1304 | zip_file_writer.interface.flush() catch |err| return f.fail( | |
| 1305 | f.location_tok, | |
| 1306 | try eb.printString("failed writing temporary zip file: {t}", .{err}), | |
| 1307 | ); | |
| 1308 | break :b zip_file_writer.moveToReader(); | |
| 1309 | }; | |
| 1340 | 1310 | |
| 1341 | 1311 | var diagnostics: std.zip.Diagnostics = .{ .allocator = f.arena.allocator() }; |
| 1342 | 1312 | // no need to deinit since we are using an arena allocator |
| 1343 | 1313 | |
| 1344 | { | |
| 1345 | var zip_file = cache_root.handle.openFile( | |
| 1346 | &zip_path, | |
| 1347 | .{}, | |
| 1348 | ) catch |err| return f.fail(f.location_tok, try eb.printString( | |
| 1349 | "failed to open temporary zip file: {s}", | |
| 1350 | .{@errorName(err)}, | |
| 1351 | )); | |
| 1352 | defer zip_file.close(); | |
| 1353 | ||
| 1354 | var zip_file_buffer: [1024]u8 = undefined; | |
| 1355 | var zip_file_reader = zip_file.reader(&zip_file_buffer); | |
| 1356 | ||
| 1357 | std.zip.extract(out_dir, &zip_file_reader, .{ | |
| 1358 | .allow_backslashes = true, | |
| 1359 | .diagnostics = &diagnostics, | |
| 1360 | }) catch |err| return f.fail(f.location_tok, try eb.printString( | |
| 1361 | "zip extract failed: {s}", | |
| 1362 | .{@errorName(err)}, | |
| 1363 | )); | |
| 1364 | } | |
| 1314 | zip_file_reader.seekTo(0) catch |err| | |
| 1315 | return f.fail(f.location_tok, try eb.printString("failed to seek temporary zip file: {t}", .{err})); | |
| 1316 | std.zip.extract(out_dir, &zip_file_reader, .{ | |
| 1317 | .allow_backslashes = true, | |
| 1318 | .diagnostics = &diagnostics, | |
| 1319 | }) catch |err| return f.fail(f.location_tok, try eb.printString("zip extract failed: {t}", .{err})); | |
| 1365 | 1320 | |
| 1366 | cache_root.handle.deleteFile(&zip_path) catch |err| return f.fail(f.location_tok, try eb.printString( | |
| 1367 | "delete temporary zip failed: {s}", | |
| 1368 | .{@errorName(err)}, | |
| 1369 | )); | |
| 1321 | cache_root.handle.deleteFile(&zip_path) catch |err| | |
| 1322 | return f.fail(f.location_tok, try eb.printString("delete temporary zip failed: {t}", .{err})); | |
| 1370 | 1323 | |
| 1371 | const res: UnpackResult = .{ .root_dir = diagnostics.root_dir }; | |
| 1372 | return res; | |
| 1324 | return .{ .root_dir = diagnostics.root_dir }; | |
| 1373 | 1325 | } |
| 1374 | 1326 | |
| 1375 | 1327 | fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!UnpackResult { |
| ... | ... | @@ -1387,10 +1339,13 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource.Git) anyerror!U |
| 1387 | 1339 | var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true }); |
| 1388 | 1340 | defer pack_file.close(); |
| 1389 | 1341 | var pack_file_buffer: [4096]u8 = undefined; |
| 1390 | var fifo = std.fifo.LinearFifo(u8, .{ .Slice = {} }).init(&pack_file_buffer); | |
| 1391 | try fifo.pump(resource.fetch_stream.reader(), pack_file.deprecatedWriter()); | |
| 1392 | ||
| 1393 | var pack_file_reader = pack_file.reader(&pack_file_buffer); | |
| 1342 | var pack_file_reader = b: { | |
| 1343 | var pack_file_writer = pack_file.writer(&pack_file_buffer); | |
| 1344 | const fetch_reader = &resource.fetch_stream.reader; | |
| 1345 | _ = try fetch_reader.streamRemaining(&pack_file_writer.interface); | |
| 1346 | try pack_file_writer.interface.flush(); | |
| 1347 | break :b pack_file_writer.moveToReader(); | |
| 1348 | }; | |
| 1394 | 1349 | |
| 1395 | 1350 | var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true }); |
| 1396 | 1351 | defer index_file.close(); |
src/Package/Fetch/git.zig+166-590| ... | ... | @@ -11,8 +11,6 @@ const Allocator = mem.Allocator; |
| 11 | 11 | const Sha1 = std.crypto.hash.Sha1; |
| 12 | 12 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 13 | 13 | const assert = std.debug.assert; |
| 14 | const zlib = std.compress.zlib; | |
| 15 | const Writer = std.io.Writer; | |
| 16 | 14 | |
| 17 | 15 | /// The ID of a Git object. |
| 18 | 16 | pub const Oid = union(Format) { |
| ... | ... | @@ -54,6 +52,7 @@ pub const Oid = union(Format) { |
| 54 | 52 | }; |
| 55 | 53 | } |
| 56 | 54 | |
| 55 | // Must be public for use from HashedReader and HashedWriter. | |
| 57 | 56 | pub fn update(hasher: *Hasher, b: []const u8) void { |
| 58 | 57 | switch (hasher.*) { |
| 59 | 58 | inline else => |*inner| inner.update(b), |
| ... | ... | @@ -65,12 +64,6 @@ pub const Oid = union(Format) { |
| 65 | 64 | inline else => |*inner, tag| @unionInit(Oid, @tagName(tag), inner.finalResult()), |
| 66 | 65 | }; |
| 67 | 66 | } |
| 68 | ||
| 69 | pub fn writer(hasher: *Hasher, buffer: []u8) Writer { | |
| 70 | return switch (hasher.*) { | |
| 71 | inline else => |*inner| inner.writer(buffer), | |
| 72 | }; | |
| 73 | } | |
| 74 | 67 | }; |
| 75 | 68 | |
| 76 | 69 | const Hashing = union(Format) { |
| ... | ... | @@ -107,30 +100,9 @@ pub const Oid = union(Format) { |
| 107 | 100 | }; |
| 108 | 101 | } |
| 109 | 102 | |
| 110 | <<<<<<< HEAD | |
| 111 | pub fn readBytes(oid_format: Format, reader: *std.io.Reader) std.io.Reader.Error!Oid { | |
| 112 | ||||||| 733b208256 | |
| 113 | pub fn readBytes(oid_format: Format, reader: anytype) @TypeOf(reader).NoEofError!Oid { | |
| 114 | ======= | |
| 115 | 103 | pub fn readBytes(oid_format: Format, reader: *std.Io.Reader) !Oid { |
| 116 | >>>>>>> origin/flate | |
| 117 | 104 | return switch (oid_format) { |
| 118 | <<<<<<< HEAD | |
| 119 | .sha1 => { | |
| 120 | var result: Oid = .{ .sha1 = undefined }; | |
| 121 | try reader.readSlice(&result.sha1); | |
| 122 | return result; | |
| 123 | }, | |
| 124 | .sha256 => { | |
| 125 | var result: Oid = .{ .sha256 = undefined }; | |
| 126 | try reader.readSlice(&result.sha256); | |
| 127 | return result; | |
| 128 | }, | |
| 129 | ||||||| 733b208256 | |
| 130 | inline else => |tag| @unionInit(Oid, @tagName(tag), try reader.readBytesNoEof(tag.byteLength())), | |
| 131 | ======= | |
| 132 | 105 | inline else => |tag| @unionInit(Oid, @tagName(tag), (try reader.takeArray(tag.byteLength())).*), |
| 133 | >>>>>>> origin/flate | |
| 134 | 106 | }; |
| 135 | 107 | } |
| 136 | 108 | |
| ... | ... | @@ -430,27 +402,14 @@ const Odb = struct { |
| 430 | 402 | |
| 431 | 403 | /// Reads the object at the current position in the database. |
| 432 | 404 | fn readObject(odb: *Odb) !Object { |
| 433 | <<<<<<< HEAD | |
| 434 | var base_offset = odb.pack_file.pos; | |
| 435 | const pack_br = &odb.pack_file.interface; | |
| 436 | ||||||| 733b208256 | |
| 437 | var base_offset = try odb.pack_file.getPos(); | |
| 438 | ======= | |
| 439 | 405 | var base_offset = odb.pack_file.logicalPos(); |
| 440 | >>>>>>> origin/flate | |
| 441 | 406 | var base_header: EntryHeader = undefined; |
| 442 | 407 | var delta_offsets: std.ArrayListUnmanaged(u64) = .empty; |
| 443 | 408 | defer delta_offsets.deinit(odb.allocator); |
| 444 | 409 | const base_object = while (true) { |
| 445 | 410 | if (odb.cache.get(base_offset)) |base_object| break base_object; |
| 446 | 411 | |
| 447 | <<<<<<< HEAD | |
| 448 | base_header = try EntryHeader.read(odb.format, pack_br); | |
| 449 | ||||||| 733b208256 | |
| 450 | base_header = try EntryHeader.read(odb.format, odb.pack_file.deprecatedReader()); | |
| 451 | ======= | |
| 452 | 412 | base_header = try EntryHeader.read(odb.format, &odb.pack_file.interface); |
| 453 | >>>>>>> origin/flate | |
| 454 | 413 | switch (base_header) { |
| 455 | 414 | .ofs_delta => |ofs_delta| { |
| 456 | 415 | try delta_offsets.append(odb.allocator, base_offset); |
| ... | ... | @@ -460,22 +419,10 @@ const Odb = struct { |
| 460 | 419 | .ref_delta => |ref_delta| { |
| 461 | 420 | try delta_offsets.append(odb.allocator, base_offset); |
| 462 | 421 | try odb.seekOid(ref_delta.base_object); |
| 463 | <<<<<<< HEAD | |
| 464 | base_offset = odb.pack_file.pos - pack_br.bufferedLen(); | |
| 465 | ||||||| 733b208256 | |
| 466 | base_offset = try odb.pack_file.getPos(); | |
| 467 | ======= | |
| 468 | 422 | base_offset = odb.pack_file.logicalPos(); |
| 469 | >>>>>>> origin/flate | |
| 470 | 423 | }, |
| 471 | 424 | else => { |
| 472 | <<<<<<< HEAD | |
| 473 | const base_data = try readObjectRaw(odb.allocator, pack_br, base_header.uncompressedLength()); | |
| 474 | ||||||| 733b208256 | |
| 475 | const base_data = try readObjectRaw(odb.allocator, odb.pack_file.deprecatedReader(), base_header.uncompressedLength()); | |
| 476 | ======= | |
| 477 | 425 | const base_data = try readObjectRaw(odb.allocator, &odb.pack_file.interface, base_header.uncompressedLength()); |
| 478 | >>>>>>> origin/flate | |
| 479 | 426 | errdefer odb.allocator.free(base_data); |
| 480 | 427 | const base_object: Object = .{ .type = base_header.objectType(), .data = base_data }; |
| 481 | 428 | try odb.cache.put(odb.allocator, base_offset, base_object); |
| ... | ... | @@ -505,14 +452,7 @@ const Odb = struct { |
| 505 | 452 | const found_index = while (start_index < end_index) { |
| 506 | 453 | const mid_index = start_index + (end_index - start_index) / 2; |
| 507 | 454 | try odb.index_file.seekTo(IndexHeader.size + mid_index * oid_length); |
| 508 | <<<<<<< HEAD | |
| 509 | var br = odb.index_file.interface().unbuffered(); | |
| 510 | const mid_oid = try Oid.readBytes(odb.format, &br); | |
| 511 | ||||||| 733b208256 | |
| 512 | const mid_oid = try Oid.readBytes(odb.format, odb.index_file.deprecatedReader()); | |
| 513 | ======= | |
| 514 | 455 | const mid_oid = try Oid.readBytes(odb.format, &odb.index_file.interface); |
| 515 | >>>>>>> origin/flate | |
| 516 | 456 | switch (mem.order(u8, mid_oid.slice(), oid.slice())) { |
| 517 | 457 | .lt => start_index = mid_index + 1, |
| 518 | 458 | .gt => end_index = mid_index, |
| ... | ... | @@ -522,28 +462,13 @@ const Odb = struct { |
| 522 | 462 | |
| 523 | 463 | const n_objects = odb.index_header.fan_out_table[255]; |
| 524 | 464 | const offset_values_start = IndexHeader.size + n_objects * (oid_length + 4); |
| 525 | var buffer: [8]u8 = undefined; | |
| 526 | 465 | try odb.index_file.seekTo(offset_values_start + found_index * 4); |
| 527 | <<<<<<< HEAD | |
| 528 | var br = odb.index_file.interface().buffered(&buffer); | |
| 529 | const l1_offset: packed struct { value: u31, big: bool } = @bitCast(try br.takeInt(u32, .big)); | |
| 530 | ||||||| 733b208256 | |
| 531 | const l1_offset: packed struct { value: u31, big: bool } = @bitCast(try odb.index_file.deprecatedReader().readInt(u32, .big)); | |
| 532 | ======= | |
| 533 | 466 | const l1_offset: packed struct { value: u31, big: bool } = @bitCast(try odb.index_file.interface.takeInt(u32, .big)); |
| 534 | >>>>>>> origin/flate | |
| 535 | 467 | const pack_offset = pack_offset: { |
| 536 | 468 | if (l1_offset.big) { |
| 537 | 469 | const l2_offset_values_start = offset_values_start + n_objects * 4; |
| 538 | 470 | try odb.index_file.seekTo(l2_offset_values_start + l1_offset.value * 4); |
| 539 | <<<<<<< HEAD | |
| 540 | br = odb.index_file.interface().buffered(&buffer); | |
| 541 | break :pack_offset try br.takeInt(u64, .big); | |
| 542 | ||||||| 733b208256 | |
| 543 | break :pack_offset try odb.index_file.deprecatedReader().readInt(u64, .big); | |
| 544 | ======= | |
| 545 | 471 | break :pack_offset try odb.index_file.interface.takeInt(u64, .big); |
| 546 | >>>>>>> origin/flate | |
| 547 | 472 | } else { |
| 548 | 473 | break :pack_offset l1_offset.value; |
| 549 | 474 | } |
| ... | ... | @@ -660,17 +585,17 @@ const ObjectCache = struct { |
| 660 | 585 | /// [protocol-common](https://git-scm.com/docs/protocol-common). The special |
| 661 | 586 | /// meanings of the delimiter and response-end packets are documented in |
| 662 | 587 | /// [protocol-v2](https://git-scm.com/docs/protocol-v2). |
| 663 | const Packet = union(enum) { | |
| 588 | pub const Packet = union(enum) { | |
| 664 | 589 | flush, |
| 665 | 590 | delimiter, |
| 666 | 591 | response_end, |
| 667 | 592 | data: []const u8, |
| 668 | 593 | |
| 669 | const max_data_length = 65516; | |
| 594 | pub const max_data_length = 65516; | |
| 670 | 595 | |
| 671 | 596 | /// Reads a packet in pkt-line format. |
| 672 | fn read(reader: *std.io.Reader, buf: *[max_data_length]u8) !Packet { | |
| 673 | const length = std.fmt.parseUnsigned(u16, &try reader.readBytesNoEof(4), 16) catch return error.InvalidPacket; | |
| 597 | fn read(reader: *std.Io.Reader) !Packet { | |
| 598 | const length = std.fmt.parseUnsigned(u16, try reader.take(4), 16) catch return error.InvalidPacket; | |
| 674 | 599 | switch (length) { |
| 675 | 600 | 0 => return .flush, |
| 676 | 601 | 1 => return .delimiter, |
| ... | ... | @@ -678,13 +603,11 @@ const Packet = union(enum) { |
| 678 | 603 | 3 => return error.InvalidPacket, |
| 679 | 604 | else => if (length - 4 > max_data_length) return error.InvalidPacket, |
| 680 | 605 | } |
| 681 | const data = buf[0 .. length - 4]; | |
| 682 | try reader.readNoEof(data); | |
| 683 | return .{ .data = data }; | |
| 606 | return .{ .data = try reader.take(length - 4) }; | |
| 684 | 607 | } |
| 685 | 608 | |
| 686 | 609 | /// Writes a packet in pkt-line format. |
| 687 | fn write(packet: Packet, writer: *Writer) !void { | |
| 610 | fn write(packet: Packet, writer: *std.Io.Writer) !void { | |
| 688 | 611 | switch (packet) { |
| 689 | 612 | .flush => try writer.writeAll("0000"), |
| 690 | 613 | .delimiter => try writer.writeAll("0001"), |
| ... | ... | @@ -732,8 +655,10 @@ pub const Session = struct { |
| 732 | 655 | allocator: Allocator, |
| 733 | 656 | transport: *std.http.Client, |
| 734 | 657 | uri: std.Uri, |
| 735 | http_headers_buffer: []u8, | |
| 658 | /// Asserted to be at least `Packet.max_data_length` | |
| 659 | response_buffer: []u8, | |
| 736 | 660 | ) !Session { |
| 661 | assert(response_buffer.len >= Packet.max_data_length); | |
| 737 | 662 | var session: Session = .{ |
| 738 | 663 | .transport = transport, |
| 739 | 664 | .location = try .init(allocator, uri), |
| ... | ... | @@ -743,7 +668,8 @@ pub const Session = struct { |
| 743 | 668 | .allocator = allocator, |
| 744 | 669 | }; |
| 745 | 670 | errdefer session.deinit(); |
| 746 | var capability_iterator = try session.getCapabilities(http_headers_buffer); | |
| 671 | var capability_iterator: CapabilityIterator = undefined; | |
| 672 | try session.getCapabilities(&capability_iterator, response_buffer); | |
| 747 | 673 | defer capability_iterator.deinit(); |
| 748 | 674 | while (try capability_iterator.next()) |capability| { |
| 749 | 675 | if (mem.eql(u8, capability.key, "agent")) { |
| ... | ... | @@ -818,7 +744,8 @@ pub const Session = struct { |
| 818 | 744 | /// |
| 819 | 745 | /// The `session.location` is updated if the server returns a redirect, so |
| 820 | 746 | /// that subsequent session functions do not need to handle redirects. |
| 821 | fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator { | |
| 747 | fn getCapabilities(session: *Session, it: *CapabilityIterator, response_buffer: []u8) !void { | |
| 748 | assert(response_buffer.len >= Packet.max_data_length); | |
| 822 | 749 | var info_refs_uri = session.location.uri; |
| 823 | 750 | { |
| 824 | 751 | const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ |
| ... | ... | @@ -832,19 +759,22 @@ pub const Session = struct { |
| 832 | 759 | info_refs_uri.fragment = null; |
| 833 | 760 | |
| 834 | 761 | const max_redirects = 3; |
| 835 | var request = try session.transport.open(.GET, info_refs_uri, .{ | |
| 836 | .redirect_behavior = @enumFromInt(max_redirects), | |
| 837 | .server_header_buffer = http_headers_buffer, | |
| 838 | .extra_headers = &.{ | |
| 839 | .{ .name = "Git-Protocol", .value = "version=2" }, | |
| 840 | }, | |
| 841 | }); | |
| 842 | errdefer request.deinit(); | |
| 843 | try request.send(); | |
| 844 | try request.finish(); | |
| 762 | it.* = .{ | |
| 763 | .request = try session.transport.request(.GET, info_refs_uri, .{ | |
| 764 | .redirect_behavior = .init(max_redirects), | |
| 765 | .extra_headers = &.{ | |
| 766 | .{ .name = "Git-Protocol", .value = "version=2" }, | |
| 767 | }, | |
| 768 | }), | |
| 769 | .reader = undefined, | |
| 770 | }; | |
| 771 | errdefer it.deinit(); | |
| 772 | const request = &it.request; | |
| 773 | try request.sendBodiless(); | |
| 845 | 774 | |
| 846 | try request.wait(); | |
| 847 | if (request.response.status != .ok) return error.ProtocolError; | |
| 775 | var redirect_buffer: [1024]u8 = undefined; | |
| 776 | var response = try request.receiveHead(&redirect_buffer); | |
| 777 | if (response.head.status != .ok) return error.ProtocolError; | |
| 848 | 778 | const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; |
| 849 | 779 | if (any_redirects_occurred) { |
| 850 | 780 | const request_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ |
| ... | ... | @@ -859,8 +789,7 @@ pub const Session = struct { |
| 859 | 789 | session.location = new_location; |
| 860 | 790 | } |
| 861 | 791 | |
| 862 | const reader = request.reader(); | |
| 863 | var buf: [Packet.max_data_length]u8 = undefined; | |
| 792 | it.reader = response.reader(response_buffer); | |
| 864 | 793 | var state: enum { response_start, response_content } = .response_start; |
| 865 | 794 | while (true) { |
| 866 | 795 | // Some Git servers (at least GitHub) include an additional |
| ... | ... | @@ -870,15 +799,15 @@ pub const Session = struct { |
| 870 | 799 | // Thus, we need to skip any such useless additional responses |
| 871 | 800 | // before we get the one we're actually looking for. The responses |
| 872 | 801 | // will be delimited by flush packets. |
| 873 | const packet = Packet.read(reader, &buf) catch |e| switch (e) { | |
| 802 | const packet = Packet.read(it.reader) catch |err| switch (err) { | |
| 874 | 803 | error.EndOfStream => return error.UnsupportedProtocol, // 'version 2' packet not found |
| 875 | else => |other| return other, | |
| 804 | else => |e| return e, | |
| 876 | 805 | }; |
| 877 | 806 | switch (packet) { |
| 878 | 807 | .flush => state = .response_start, |
| 879 | 808 | .data => |data| switch (state) { |
| 880 | 809 | .response_start => if (mem.eql(u8, Packet.normalizeText(data), "version 2")) { |
| 881 | return .{ .request = request }; | |
| 810 | return; | |
| 882 | 811 | } else { |
| 883 | 812 | state = .response_content; |
| 884 | 813 | }, |
| ... | ... | @@ -891,7 +820,7 @@ pub const Session = struct { |
| 891 | 820 | |
| 892 | 821 | const CapabilityIterator = struct { |
| 893 | 822 | request: std.http.Client.Request, |
| 894 | buf: [Packet.max_data_length]u8 = undefined, | |
| 823 | reader: *std.Io.Reader, | |
| 895 | 824 | |
| 896 | 825 | const Capability = struct { |
| 897 | 826 | key: []const u8, |
| ... | ... | @@ -905,13 +834,13 @@ pub const Session = struct { |
| 905 | 834 | } |
| 906 | 835 | }; |
| 907 | 836 | |
| 908 | fn deinit(iterator: *CapabilityIterator) void { | |
| 909 | iterator.request.deinit(); | |
| 910 | iterator.* = undefined; | |
| 837 | fn deinit(it: *CapabilityIterator) void { | |
| 838 | it.request.deinit(); | |
| 839 | it.* = undefined; | |
| 911 | 840 | } |
| 912 | 841 | |
| 913 | fn next(iterator: *CapabilityIterator) !?Capability { | |
| 914 | switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { | |
| 842 | fn next(it: *CapabilityIterator) !?Capability { | |
| 843 | switch (try Packet.read(it.reader)) { | |
| 915 | 844 | .flush => return null, |
| 916 | 845 | .data => |data| return Capability.parse(Packet.normalizeText(data)), |
| 917 | 846 | else => return error.UnexpectedPacket, |
| ... | ... | @@ -929,11 +858,13 @@ pub const Session = struct { |
| 929 | 858 | include_symrefs: bool = false, |
| 930 | 859 | /// Whether to include the peeled object ID for returned tag refs. |
| 931 | 860 | include_peeled: bool = false, |
| 932 | server_header_buffer: []u8, | |
| 861 | /// Asserted to be at least `Packet.max_data_length`. | |
| 862 | buffer: []u8, | |
| 933 | 863 | }; |
| 934 | 864 | |
| 935 | 865 | /// Returns an iterator over refs known to the server. |
| 936 | pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator { | |
| 866 | pub fn listRefs(session: Session, it: *RefIterator, options: ListRefsOptions) !void { | |
| 867 | assert(options.buffer.len >= Packet.max_data_length); | |
| 937 | 868 | var upload_pack_uri = session.location.uri; |
| 938 | 869 | { |
| 939 | 870 | const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ |
| ... | ... | @@ -946,61 +877,56 @@ pub const Session = struct { |
| 946 | 877 | upload_pack_uri.query = null; |
| 947 | 878 | upload_pack_uri.fragment = null; |
| 948 | 879 | |
| 949 | var body: std.io.Writer.Allocating = .init(session.allocator); | |
| 950 | const body_writer = &body.interface; | |
| 951 | defer body.deinit(); | |
| 952 | try Packet.write(.{ .data = "command=ls-refs\n" }, body_writer); | |
| 880 | var body: std.Io.Writer = .fixed(options.buffer); | |
| 881 | try Packet.write(.{ .data = "command=ls-refs\n" }, &body); | |
| 953 | 882 | if (session.supports_agent) { |
| 954 | try Packet.write(.{ .data = agent_capability }, body_writer); | |
| 883 | try Packet.write(.{ .data = agent_capability }, &body); | |
| 955 | 884 | } |
| 956 | 885 | { |
| 957 | const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={s}\n", .{@tagName(session.object_format)}); | |
| 886 | const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={t}\n", .{ | |
| 887 | session.object_format, | |
| 888 | }); | |
| 958 | 889 | defer session.allocator.free(object_format_packet); |
| 959 | try Packet.write(.{ .data = object_format_packet }, body_writer); | |
| 890 | try Packet.write(.{ .data = object_format_packet }, &body); | |
| 960 | 891 | } |
| 961 | try Packet.write(.delimiter, body_writer); | |
| 892 | try Packet.write(.delimiter, &body); | |
| 962 | 893 | for (options.ref_prefixes) |ref_prefix| { |
| 963 | 894 | const ref_prefix_packet = try std.fmt.allocPrint(session.allocator, "ref-prefix {s}\n", .{ref_prefix}); |
| 964 | 895 | defer session.allocator.free(ref_prefix_packet); |
| 965 | try Packet.write(.{ .data = ref_prefix_packet }, body_writer); | |
| 896 | try Packet.write(.{ .data = ref_prefix_packet }, &body); | |
| 966 | 897 | } |
| 967 | 898 | if (options.include_symrefs) { |
| 968 | try Packet.write(.{ .data = "symrefs\n" }, body_writer); | |
| 899 | try Packet.write(.{ .data = "symrefs\n" }, &body); | |
| 969 | 900 | } |
| 970 | 901 | if (options.include_peeled) { |
| 971 | try Packet.write(.{ .data = "peel\n" }, body_writer); | |
| 902 | try Packet.write(.{ .data = "peel\n" }, &body); | |
| 972 | 903 | } |
| 973 | try Packet.write(.flush, body_writer); | |
| 974 | ||
| 975 | var request = try session.transport.open(.POST, upload_pack_uri, .{ | |
| 976 | .redirect_behavior = .unhandled, | |
| 977 | .server_header_buffer = options.server_header_buffer, | |
| 978 | .extra_headers = &.{ | |
| 979 | .{ .name = "Content-Type", .value = "application/x-git-upload-pack-request" }, | |
| 980 | .{ .name = "Git-Protocol", .value = "version=2" }, | |
| 981 | }, | |
| 982 | }); | |
| 983 | errdefer request.deinit(); | |
| 984 | const written = body.getWritten(); | |
| 985 | request.transfer_encoding = .{ .content_length = written.len }; | |
| 986 | try request.send(); | |
| 987 | var w = request.writer().unbuffered(); | |
| 988 | try w.writeAll(written); | |
| 989 | try request.finish(); | |
| 990 | ||
| 991 | try request.wait(); | |
| 992 | if (request.response.status != .ok) return error.ProtocolError; | |
| 993 | ||
| 994 | return .{ | |
| 904 | try Packet.write(.flush, &body); | |
| 905 | ||
| 906 | it.* = .{ | |
| 907 | .request = try session.transport.request(.POST, upload_pack_uri, .{ | |
| 908 | .redirect_behavior = .unhandled, | |
| 909 | .extra_headers = &.{ | |
| 910 | .{ .name = "Content-Type", .value = "application/x-git-upload-pack-request" }, | |
| 911 | .{ .name = "Git-Protocol", .value = "version=2" }, | |
| 912 | }, | |
| 913 | }), | |
| 914 | .reader = undefined, | |
| 995 | 915 | .format = session.object_format, |
| 996 | .request = request, | |
| 997 | 916 | }; |
| 917 | const request = &it.request; | |
| 918 | errdefer request.deinit(); | |
| 919 | try request.sendBodyComplete(body.buffered()); | |
| 920 | ||
| 921 | var response = try request.receiveHead(options.buffer); | |
| 922 | if (response.head.status != .ok) return error.ProtocolError; | |
| 923 | it.reader = response.reader(options.buffer); | |
| 998 | 924 | } |
| 999 | 925 | |
| 1000 | 926 | pub const RefIterator = struct { |
| 1001 | 927 | format: Oid.Format, |
| 1002 | 928 | request: std.http.Client.Request, |
| 1003 | buf: [Packet.max_data_length]u8 = undefined, | |
| 929 | reader: *std.Io.Reader, | |
| 1004 | 930 | |
| 1005 | 931 | pub const Ref = struct { |
| 1006 | 932 | oid: Oid, |
| ... | ... | @@ -1014,13 +940,13 @@ pub const Session = struct { |
| 1014 | 940 | iterator.* = undefined; |
| 1015 | 941 | } |
| 1016 | 942 | |
| 1017 | pub fn next(iterator: *RefIterator) !?Ref { | |
| 1018 | switch (try Packet.read(iterator.request.reader(), &iterator.buf)) { | |
| 943 | pub fn next(it: *RefIterator) !?Ref { | |
| 944 | switch (try Packet.read(it.reader)) { | |
| 1019 | 945 | .flush => return null, |
| 1020 | 946 | .data => |data| { |
| 1021 | 947 | const ref_data = Packet.normalizeText(data); |
| 1022 | 948 | const oid_sep_pos = mem.indexOfScalar(u8, ref_data, ' ') orelse return error.InvalidRefPacket; |
| 1023 | const oid = Oid.parse(iterator.format, data[0..oid_sep_pos]) catch return error.InvalidRefPacket; | |
| 949 | const oid = Oid.parse(it.format, data[0..oid_sep_pos]) catch return error.InvalidRefPacket; | |
| 1024 | 950 | |
| 1025 | 951 | const name_sep_pos = mem.indexOfScalarPos(u8, ref_data, oid_sep_pos + 1, ' ') orelse ref_data.len; |
| 1026 | 952 | const name = ref_data[oid_sep_pos + 1 .. name_sep_pos]; |
| ... | ... | @@ -1034,7 +960,7 @@ pub const Session = struct { |
| 1034 | 960 | if (mem.startsWith(u8, attribute, "symref-target:")) { |
| 1035 | 961 | symref_target = attribute["symref-target:".len..]; |
| 1036 | 962 | } else if (mem.startsWith(u8, attribute, "peeled:")) { |
| 1037 | peeled = Oid.parse(iterator.format, attribute["peeled:".len..]) catch return error.InvalidRefPacket; | |
| 963 | peeled = Oid.parse(it.format, attribute["peeled:".len..]) catch return error.InvalidRefPacket; | |
| 1038 | 964 | } |
| 1039 | 965 | last_sep_pos = next_sep_pos; |
| 1040 | 966 | } |
| ... | ... | @@ -1050,9 +976,12 @@ pub const Session = struct { |
| 1050 | 976 | /// performed if the server supports it. |
| 1051 | 977 | pub fn fetch( |
| 1052 | 978 | session: Session, |
| 979 | fs: *FetchStream, | |
| 1053 | 980 | wants: []const []const u8, |
| 1054 | http_headers_buffer: []u8, | |
| 1055 | ) !FetchStream { | |
| 981 | /// Asserted to be at least `Packet.max_data_length`. | |
| 982 | response_buffer: []u8, | |
| 983 | ) !void { | |
| 984 | assert(response_buffer.len >= Packet.max_data_length); | |
| 1056 | 985 | var upload_pack_uri = session.location.uri; |
| 1057 | 986 | { |
| 1058 | 987 | const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ |
| ... | ... | @@ -1065,65 +994,71 @@ pub const Session = struct { |
| 1065 | 994 | upload_pack_uri.query = null; |
| 1066 | 995 | upload_pack_uri.fragment = null; |
| 1067 | 996 | |
| 1068 | var body: std.io.Writer.Allocating = .init(session.allocator); | |
| 1069 | defer body.deinit(); | |
| 1070 | const body_writer = &body.interface; | |
| 1071 | try Packet.write(.{ .data = "command=fetch\n" }, body_writer); | |
| 997 | var body: std.Io.Writer = .fixed(response_buffer); | |
| 998 | try Packet.write(.{ .data = "command=fetch\n" }, &body); | |
| 1072 | 999 | if (session.supports_agent) { |
| 1073 | try Packet.write(.{ .data = agent_capability }, body_writer); | |
| 1000 | try Packet.write(.{ .data = agent_capability }, &body); | |
| 1074 | 1001 | } |
| 1075 | 1002 | { |
| 1076 | 1003 | const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={s}\n", .{@tagName(session.object_format)}); |
| 1077 | 1004 | defer session.allocator.free(object_format_packet); |
| 1078 | try Packet.write(.{ .data = object_format_packet }, body_writer); | |
| 1005 | try Packet.write(.{ .data = object_format_packet }, &body); | |
| 1079 | 1006 | } |
| 1080 | try Packet.write(.delimiter, body_writer); | |
| 1007 | try Packet.write(.delimiter, &body); | |
| 1081 | 1008 | // Our packfile parser supports the OFS_DELTA object type |
| 1082 | try Packet.write(.{ .data = "ofs-delta\n" }, body_writer); | |
| 1009 | try Packet.write(.{ .data = "ofs-delta\n" }, &body); | |
| 1083 | 1010 | // We do not currently convey server progress information to the user |
| 1084 | try Packet.write(.{ .data = "no-progress\n" }, body_writer); | |
| 1011 | try Packet.write(.{ .data = "no-progress\n" }, &body); | |
| 1085 | 1012 | if (session.supports_shallow) { |
| 1086 | try Packet.write(.{ .data = "deepen 1\n" }, body_writer); | |
| 1013 | try Packet.write(.{ .data = "deepen 1\n" }, &body); | |
| 1087 | 1014 | } |
| 1088 | 1015 | for (wants) |want| { |
| 1089 | 1016 | var buf: [Packet.max_data_length]u8 = undefined; |
| 1090 | 1017 | const arg = std.fmt.bufPrint(&buf, "want {s}\n", .{want}) catch unreachable; |
| 1091 | try Packet.write(.{ .data = arg }, body_writer); | |
| 1018 | try Packet.write(.{ .data = arg }, &body); | |
| 1092 | 1019 | } |
| 1093 | try Packet.write(.{ .data = "done\n" }, body_writer); | |
| 1094 | try Packet.write(.flush, body_writer); | |
| 1095 | ||
| 1096 | var request = try session.transport.open(.POST, upload_pack_uri, .{ | |
| 1097 | .redirect_behavior = .not_allowed, | |
| 1098 | .server_header_buffer = http_headers_buffer, | |
| 1099 | .extra_headers = &.{ | |
| 1100 | .{ .name = "Content-Type", .value = "application/x-git-upload-pack-request" }, | |
| 1101 | .{ .name = "Git-Protocol", .value = "version=2" }, | |
| 1102 | }, | |
| 1103 | }); | |
| 1020 | try Packet.write(.{ .data = "done\n" }, &body); | |
| 1021 | try Packet.write(.flush, &body); | |
| 1022 | ||
| 1023 | fs.* = .{ | |
| 1024 | .request = try session.transport.request(.POST, upload_pack_uri, .{ | |
| 1025 | .redirect_behavior = .not_allowed, | |
| 1026 | .extra_headers = &.{ | |
| 1027 | .{ .name = "Content-Type", .value = "application/x-git-upload-pack-request" }, | |
| 1028 | .{ .name = "Git-Protocol", .value = "version=2" }, | |
| 1029 | }, | |
| 1030 | }), | |
| 1031 | .input = undefined, | |
| 1032 | .reader = undefined, | |
| 1033 | .remaining_len = undefined, | |
| 1034 | }; | |
| 1035 | const request = &fs.request; | |
| 1104 | 1036 | errdefer request.deinit(); |
| 1105 | const written = body.getWritten(); | |
| 1106 | request.transfer_encoding = .{ .content_length = written.len }; | |
| 1107 | try request.send(); | |
| 1108 | var w = request.writer().unbuffered(); | |
| 1109 | try w.writeAll(written); | |
| 1110 | try request.finish(); | |
| 1111 | 1037 | |
| 1112 | try request.wait(); | |
| 1113 | if (request.response.status != .ok) return error.ProtocolError; | |
| 1038 | try request.sendBodyComplete(body.buffered()); | |
| 1114 | 1039 | |
| 1115 | const reader = request.reader(); | |
| 1040 | var response = try request.receiveHead(&.{}); | |
| 1041 | if (response.head.status != .ok) return error.ProtocolError; | |
| 1042 | ||
| 1043 | const reader = response.reader(response_buffer); | |
| 1116 | 1044 | // We are not interested in any of the sections of the returned fetch |
| 1117 | 1045 | // data other than the packfile section, since we aren't doing anything |
| 1118 | 1046 | // complex like ref negotiation (this is a fresh clone). |
| 1119 | 1047 | var state: enum { section_start, section_content } = .section_start; |
| 1120 | 1048 | while (true) { |
| 1121 | var buf: [Packet.max_data_length]u8 = undefined; | |
| 1122 | const packet = try Packet.read(reader, &buf); | |
| 1049 | const packet = try Packet.read(reader); | |
| 1123 | 1050 | switch (state) { |
| 1124 | 1051 | .section_start => switch (packet) { |
| 1125 | 1052 | .data => |data| if (mem.eql(u8, Packet.normalizeText(data), "packfile")) { |
| 1126 | return .{ .request = request }; | |
| 1053 | fs.input = reader; | |
| 1054 | fs.reader = .{ | |
| 1055 | .buffer = &.{}, | |
| 1056 | .vtable = &.{ .stream = FetchStream.stream }, | |
| 1057 | .seek = 0, | |
| 1058 | .end = 0, | |
| 1059 | }; | |
| 1060 | fs.remaining_len = 0; | |
| 1061 | return; | |
| 1127 | 1062 | } else { |
| 1128 | 1063 | state = .section_content; |
| 1129 | 1064 | }, |
| ... | ... | @@ -1140,20 +1075,23 @@ pub const Session = struct { |
| 1140 | 1075 | |
| 1141 | 1076 | pub const FetchStream = struct { |
| 1142 | 1077 | request: std.http.Client.Request, |
| 1143 | buf: [Packet.max_data_length]u8 = undefined, | |
| 1144 | pos: usize = 0, | |
| 1145 | len: usize = 0, | |
| 1078 | input: *std.Io.Reader, | |
| 1079 | reader: std.Io.Reader, | |
| 1080 | err: ?Error = null, | |
| 1081 | remaining_len: usize, | |
| 1146 | 1082 | |
| 1147 | pub fn deinit(stream: *FetchStream) void { | |
| 1148 | stream.request.deinit(); | |
| 1083 | pub fn deinit(fs: *FetchStream) void { | |
| 1084 | fs.request.deinit(); | |
| 1149 | 1085 | } |
| 1150 | 1086 | |
| 1151 | pub const ReadError = std.http.Client.Request.ReadError || error{ | |
| 1087 | pub const Error = error{ | |
| 1152 | 1088 | InvalidPacket, |
| 1153 | 1089 | ProtocolError, |
| 1154 | 1090 | UnexpectedPacket, |
| 1091 | WriteFailed, | |
| 1092 | ReadFailed, | |
| 1093 | EndOfStream, | |
| 1155 | 1094 | }; |
| 1156 | pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read); | |
| 1157 | 1095 | |
| 1158 | 1096 | const StreamCode = enum(u8) { |
| 1159 | 1097 | pack_data = 1, |
| ... | ... | @@ -1162,33 +1100,41 @@ pub const Session = struct { |
| 1162 | 1100 | _, |
| 1163 | 1101 | }; |
| 1164 | 1102 | |
| 1165 | pub fn reader(stream: *FetchStream) Reader { | |
| 1166 | return .{ .context = stream }; | |
| 1167 | } | |
| 1168 | ||
| 1169 | pub fn read(stream: *FetchStream, buf: []u8) !usize { | |
| 1170 | if (stream.pos == stream.len) { | |
| 1103 | pub fn stream(r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize { | |
| 1104 | const fs: *FetchStream = @alignCast(@fieldParentPtr("reader", r)); | |
| 1105 | const input = fs.input; | |
| 1106 | if (fs.remaining_len == 0) { | |
| 1171 | 1107 | while (true) { |
| 1172 | switch (try Packet.read(stream.request.reader(), &stream.buf)) { | |
| 1173 | .flush => return 0, | |
| 1108 | switch (Packet.read(input) catch |err| { | |
| 1109 | fs.err = err; | |
| 1110 | return error.ReadFailed; | |
| 1111 | }) { | |
| 1112 | .flush => return error.EndOfStream, | |
| 1174 | 1113 | .data => |data| if (data.len > 1) switch (@as(StreamCode, @enumFromInt(data[0]))) { |
| 1175 | 1114 | .pack_data => { |
| 1176 | stream.pos = 1; | |
| 1177 | stream.len = data.len; | |
| 1115 | input.toss(1); | |
| 1116 | fs.remaining_len = data.len; | |
| 1178 | 1117 | break; |
| 1179 | 1118 | }, |
| 1180 | .fatal_error => return error.ProtocolError, | |
| 1119 | .fatal_error => { | |
| 1120 | fs.err = error.ProtocolError; | |
| 1121 | return error.ReadFailed; | |
| 1122 | }, | |
| 1181 | 1123 | else => {}, |
| 1182 | 1124 | }, |
| 1183 | else => return error.UnexpectedPacket, | |
| 1125 | else => { | |
| 1126 | fs.err = error.UnexpectedPacket; | |
| 1127 | return error.ReadFailed; | |
| 1128 | }, | |
| 1184 | 1129 | } |
| 1185 | 1130 | } |
| 1186 | 1131 | } |
| 1187 | ||
| 1188 | const size = @min(buf.len, stream.len - stream.pos); | |
| 1189 | @memcpy(buf[0..size], stream.buf[stream.pos .. stream.pos + size]); | |
| 1190 | stream.pos += size; | |
| 1191 | return size; | |
| 1132 | const buf = limit.slice(try w.writableSliceGreedy(1)); | |
| 1133 | const n = @min(buf.len, fs.remaining_len); | |
| 1134 | @memcpy(buf[0..n], input.buffered()[0..n]); | |
| 1135 | input.toss(n); | |
| 1136 | fs.remaining_len -= n; | |
| 1137 | return n; | |
| 1192 | 1138 | } |
| 1193 | 1139 | }; |
| 1194 | 1140 | }; |
| ... | ... | @@ -1199,23 +1145,6 @@ const PackHeader = struct { |
| 1199 | 1145 | const signature = "PACK"; |
| 1200 | 1146 | const supported_version = 2; |
| 1201 | 1147 | |
| 1202 | <<<<<<< HEAD | |
| 1203 | fn read(reader: *std.io.Reader) !PackHeader { | |
| 1204 | const actual_signature = try reader.take(4); | |
| 1205 | if (!mem.eql(u8, actual_signature, signature)) return error.InvalidHeader; | |
| 1206 | const version = try reader.takeInt(u32, .big); | |
| 1207 | ||||||| 733b208256 | |
| 1208 | fn read(reader: anytype) !PackHeader { | |
| 1209 | const actual_signature = reader.readBytesNoEof(4) catch |e| switch (e) { | |
| 1210 | error.EndOfStream => return error.InvalidHeader, | |
| 1211 | else => |other| return other, | |
| 1212 | }; | |
| 1213 | if (!mem.eql(u8, &actual_signature, signature)) return error.InvalidHeader; | |
| 1214 | const version = reader.readInt(u32, .big) catch |e| switch (e) { | |
| 1215 | error.EndOfStream => return error.InvalidHeader, | |
| 1216 | else => |other| return other, | |
| 1217 | }; | |
| 1218 | ======= | |
| 1219 | 1148 | fn read(reader: *std.Io.Reader) !PackHeader { |
| 1220 | 1149 | const actual_signature = reader.take(4) catch |e| switch (e) { |
| 1221 | 1150 | error.EndOfStream => return error.InvalidHeader, |
| ... | ... | @@ -1226,21 +1155,11 @@ const PackHeader = struct { |
| 1226 | 1155 | error.EndOfStream => return error.InvalidHeader, |
| 1227 | 1156 | else => |other| return other, |
| 1228 | 1157 | }; |
| 1229 | >>>>>>> origin/flate | |
| 1230 | 1158 | if (version != supported_version) return error.UnsupportedVersion; |
| 1231 | <<<<<<< HEAD | |
| 1232 | const total_objects = try reader.takeInt(u32, .big); | |
| 1233 | ||||||| 733b208256 | |
| 1234 | const total_objects = reader.readInt(u32, .big) catch |e| switch (e) { | |
| 1235 | error.EndOfStream => return error.InvalidHeader, | |
| 1236 | else => |other| return other, | |
| 1237 | }; | |
| 1238 | ======= | |
| 1239 | 1159 | const total_objects = reader.takeInt(u32, .big) catch |e| switch (e) { |
| 1240 | 1160 | error.EndOfStream => return error.InvalidHeader, |
| 1241 | 1161 | else => |other| return other, |
| 1242 | 1162 | }; |
| 1243 | >>>>>>> origin/flate | |
| 1244 | 1163 | return .{ .total_objects = total_objects }; |
| 1245 | 1164 | } |
| 1246 | 1165 | }; |
| ... | ... | @@ -1289,30 +1208,13 @@ const EntryHeader = union(Type) { |
| 1289 | 1208 | }; |
| 1290 | 1209 | } |
| 1291 | 1210 | |
| 1292 | <<<<<<< HEAD | |
| 1293 | fn read(format: Oid.Format, reader: *std.io.Reader) !EntryHeader { | |
| 1294 | ||||||| 733b208256 | |
| 1295 | fn read(format: Oid.Format, reader: anytype) !EntryHeader { | |
| 1296 | ======= | |
| 1297 | 1211 | fn read(format: Oid.Format, reader: *std.Io.Reader) !EntryHeader { |
| 1298 | >>>>>>> origin/flate | |
| 1299 | 1212 | const InitialByte = packed struct { len: u4, type: u3, has_next: bool }; |
| 1300 | <<<<<<< HEAD | |
| 1301 | const initial: InitialByte = @bitCast(try reader.takeByte()); | |
| 1302 | const rest_len = if (initial.has_next) try readSizeVarInt(reader) else 0; | |
| 1303 | ||||||| 733b208256 | |
| 1304 | const initial: InitialByte = @bitCast(reader.readByte() catch |e| switch (e) { | |
| 1305 | error.EndOfStream => return error.InvalidFormat, | |
| 1306 | else => |other| return other, | |
| 1307 | }); | |
| 1308 | const rest_len = if (initial.has_next) try readSizeVarInt(reader) else 0; | |
| 1309 | ======= | |
| 1310 | 1213 | const initial: InitialByte = @bitCast(reader.takeByte() catch |e| switch (e) { |
| 1311 | 1214 | error.EndOfStream => return error.InvalidFormat, |
| 1312 | 1215 | else => |other| return other, |
| 1313 | 1216 | }); |
| 1314 | 1217 | const rest_len = if (initial.has_next) try reader.takeLeb128(u64) else 0; |
| 1315 | >>>>>>> origin/flate | |
| 1316 | 1218 | var uncompressed_length: u64 = initial.len; |
| 1317 | 1219 | uncompressed_length |= std.math.shlExact(u64, rest_len, 4) catch return error.InvalidFormat; |
| 1318 | 1220 | const @"type" = std.enums.fromInt(EntryHeader.Type, initial.type) orelse return error.InvalidFormat; |
| ... | ... | @@ -1335,47 +1237,9 @@ const EntryHeader = union(Type) { |
| 1335 | 1237 | } |
| 1336 | 1238 | }; |
| 1337 | 1239 | |
| 1338 | <<<<<<< HEAD | |
| 1339 | fn readSizeVarInt(r: *std.io.Reader) !u64 { | |
| 1340 | ||||||| 733b208256 | |
| 1341 | fn readSizeVarInt(r: anytype) !u64 { | |
| 1342 | ======= | |
| 1343 | 1240 | fn readOffsetVarInt(r: *std.Io.Reader) !u64 { |
| 1344 | >>>>>>> origin/flate | |
| 1345 | 1241 | const Byte = packed struct { value: u7, has_next: bool }; |
| 1346 | <<<<<<< HEAD | |
| 1347 | 1242 | var b: Byte = @bitCast(try r.takeByte()); |
| 1348 | var value: u64 = b.value; | |
| 1349 | var shift: u6 = 0; | |
| 1350 | while (b.has_next) { | |
| 1351 | b = @bitCast(try r.takeByte()); | |
| 1352 | shift = std.math.add(u6, shift, 7) catch return error.InvalidFormat; | |
| 1353 | value |= @as(u64, b.value) << shift; | |
| 1354 | } | |
| 1355 | return value; | |
| 1356 | } | |
| 1357 | ||
| 1358 | fn readOffsetVarInt(r: *std.io.Reader) !u64 { | |
| 1359 | const Byte = packed struct { value: u7, has_next: bool }; | |
| 1360 | var b: Byte = @bitCast(try r.takeByte()); | |
| 1361 | ||||||| 733b208256 | |
| 1362 | var b: Byte = @bitCast(try r.readByte()); | |
| 1363 | var value: u64 = b.value; | |
| 1364 | var shift: u6 = 0; | |
| 1365 | while (b.has_next) { | |
| 1366 | b = @bitCast(try r.readByte()); | |
| 1367 | shift = std.math.add(u6, shift, 7) catch return error.InvalidFormat; | |
| 1368 | value |= @as(u64, b.value) << shift; | |
| 1369 | } | |
| 1370 | return value; | |
| 1371 | } | |
| 1372 | ||
| 1373 | fn readOffsetVarInt(r: anytype) !u64 { | |
| 1374 | const Byte = packed struct { value: u7, has_next: bool }; | |
| 1375 | var b: Byte = @bitCast(try r.readByte()); | |
| 1376 | ======= | |
| 1377 | var b: Byte = @bitCast(try r.takeByte()); | |
| 1378 | >>>>>>> origin/flate | |
| 1379 | 1243 | var value: u64 = b.value; |
| 1380 | 1244 | while (b.has_next) { |
| 1381 | 1245 | b = @bitCast(try r.takeByte()); |
| ... | ... | @@ -1392,37 +1256,12 @@ const IndexHeader = struct { |
| 1392 | 1256 | const supported_version = 2; |
| 1393 | 1257 | const size = 4 + 4 + @sizeOf([256]u32); |
| 1394 | 1258 | |
| 1395 | <<<<<<< HEAD | |
| 1396 | fn read(index_header: *IndexHeader, br: *std.io.Reader) !void { | |
| 1397 | const sig = try br.take(4); | |
| 1398 | if (!mem.eql(u8, sig, signature)) return error.InvalidHeader; | |
| 1399 | const version = try br.takeInt(u32, .big); | |
| 1400 | ||||||| 733b208256 | |
| 1401 | fn read(reader: anytype) !IndexHeader { | |
| 1402 | var header_bytes = try reader.readBytesNoEof(size); | |
| 1403 | if (!mem.eql(u8, header_bytes[0..4], signature)) return error.InvalidHeader; | |
| 1404 | const version = mem.readInt(u32, header_bytes[4..8], .big); | |
| 1405 | ======= | |
| 1406 | 1259 | fn read(index_header: *IndexHeader, reader: *std.Io.Reader) !void { |
| 1407 | 1260 | const sig = try reader.take(4); |
| 1408 | 1261 | if (!mem.eql(u8, sig, signature)) return error.InvalidHeader; |
| 1409 | 1262 | const version = try reader.takeInt(u32, .big); |
| 1410 | >>>>>>> origin/flate | |
| 1411 | 1263 | if (version != supported_version) return error.UnsupportedVersion; |
| 1412 | <<<<<<< HEAD | |
| 1413 | try br.readSliceEndian(u32, &index_header.fan_out_table, .big); | |
| 1414 | ||||||| 733b208256 | |
| 1415 | ||
| 1416 | var fan_out_table: [256]u32 = undefined; | |
| 1417 | var fan_out_table_stream = std.io.fixedBufferStream(header_bytes[8..]); | |
| 1418 | const fan_out_table_reader = fan_out_table_stream.reader(); | |
| 1419 | for (&fan_out_table) |*entry| { | |
| 1420 | entry.* = fan_out_table_reader.readInt(u32, .big) catch unreachable; | |
| 1421 | } | |
| 1422 | return .{ .fan_out_table = fan_out_table }; | |
| 1423 | ======= | |
| 1424 | 1264 | try reader.readSliceEndian(u32, &index_header.fan_out_table, .big); |
| 1425 | >>>>>>> origin/flate | |
| 1426 | 1265 | } |
| 1427 | 1266 | }; |
| 1428 | 1267 | |
| ... | ... | @@ -1491,18 +1330,8 @@ pub fn indexPack( |
| 1491 | 1330 | } |
| 1492 | 1331 | @memset(fan_out_table[fan_out_index..], count); |
| 1493 | 1332 | |
| 1494 | <<<<<<< HEAD | |
| 1495 | var index_writer_bw = index_writer.writer(&.{}); | |
| 1496 | var index_hashed_writer = index_writer_bw.hashed(Oid.Hasher.init(format)); | |
| 1497 | var write_buffer: [256]u8 = undefined; | |
| 1498 | var writer = index_hashed_writer.writer(&write_buffer); | |
| 1499 | ||||||| 733b208256 | |
| 1500 | var index_hashed_writer = hashedWriter(index_writer, Oid.Hasher.init(format)); | |
| 1501 | const writer = index_hashed_writer.writer(); | |
| 1502 | ======= | |
| 1503 | 1333 | var index_hashed_writer = std.Io.Writer.hashed(&index_writer.interface, Oid.Hasher.init(format), &.{}); |
| 1504 | 1334 | const writer = &index_hashed_writer.writer; |
| 1505 | >>>>>>> origin/flate | |
| 1506 | 1335 | try writer.writeAll(IndexHeader.signature); |
| 1507 | 1336 | try writer.writeInt(u32, IndexHeader.supported_version, .big); |
| 1508 | 1337 | for (fan_out_table) |fan_out_entry| { |
| ... | ... | @@ -1534,16 +1363,9 @@ pub fn indexPack( |
| 1534 | 1363 | } |
| 1535 | 1364 | |
| 1536 | 1365 | try writer.writeAll(pack_checksum.slice()); |
| 1537 | try writer.flush(); | |
| 1538 | 1366 | const index_checksum = index_hashed_writer.hasher.finalResult(); |
| 1539 | <<<<<<< HEAD | |
| 1540 | try index_writer_bw.writeAll(index_checksum.slice()); | |
| 1541 | ||||||| 733b208256 | |
| 1542 | try index_writer.writeAll(index_checksum.slice()); | |
| 1543 | ======= | |
| 1544 | 1367 | try index_writer.interface.writeAll(index_checksum.slice()); |
| 1545 | 1368 | try index_writer.end(); |
| 1546 | >>>>>>> origin/flate | |
| 1547 | 1369 | } |
| 1548 | 1370 | |
| 1549 | 1371 | /// Performs the first pass over the packfile data for index construction. |
| ... | ... | @@ -1557,113 +1379,37 @@ fn indexPackFirstPass( |
| 1557 | 1379 | index_entries: *std.AutoHashMapUnmanaged(Oid, IndexEntry), |
| 1558 | 1380 | pending_deltas: *std.ArrayListUnmanaged(IndexEntry), |
| 1559 | 1381 | ) !Oid { |
| 1560 | <<<<<<< HEAD | |
| 1561 | var pack_buffer: [2048]u8 = undefined; // Reasonably large buffer for file system. | |
| 1562 | var pack_hashed = pack.interface.hashed(Oid.Hasher.init(format), &pack_buffer); | |
| 1563 | ||||||| 733b208256 | |
| 1564 | var pack_buffered_reader = std.io.bufferedReader(pack.deprecatedReader()); | |
| 1565 | var pack_counting_reader = std.io.countingReader(pack_buffered_reader.reader()); | |
| 1566 | var pack_hashed_reader = hashedReader(pack_counting_reader.reader(), Oid.Hasher.init(format)); | |
| 1567 | const pack_reader = pack_hashed_reader.reader(); | |
| 1568 | ======= | |
| 1569 | 1382 | var flate_buffer: [std.compress.flate.max_window_len]u8 = undefined; |
| 1570 | 1383 | var pack_buffer: [2048]u8 = undefined; // Reasonably large buffer for file system. |
| 1571 | 1384 | var pack_hashed = pack.interface.hashed(Oid.Hasher.init(format), &pack_buffer); |
| 1572 | >>>>>>> origin/flate | |
| 1573 | 1385 | |
| 1574 | <<<<<<< HEAD | |
| 1575 | const pack_header = try PackHeader.read(&pack_hashed.interface); | |
| 1576 | ||||||| 733b208256 | |
| 1577 | const pack_header = try PackHeader.read(pack_reader); | |
| 1578 | ======= | |
| 1579 | 1386 | const pack_header = try PackHeader.read(&pack_hashed.reader); |
| 1580 | >>>>>>> origin/flate | |
| 1581 | 1387 | |
| 1582 | <<<<<<< HEAD | |
| 1583 | for (0..pack_header.total_objects) |_| { | |
| 1584 | const entry_offset = pack.pos - pack_hashed.interface.buffered().len; | |
| 1585 | var entry_buffer: [64]u8 = undefined; // Buffer only needed for loading EntryHeader. | |
| 1586 | var entry_crc32_reader = pack_hashed.interface.hashed(std.hash.Crc32.init(), &entry_buffer); | |
| 1587 | const entry_crc32_br = &entry_crc32_reader.interface; | |
| 1588 | const entry_header = try EntryHeader.read(format, &entry_crc32_br); | |
| 1589 | var entry_decompress_stream: zlib.Decompressor = .init(&entry_crc32_br); | |
| 1590 | // Decompress uses large output buffer; no input buffer needed. | |
| 1591 | var entry_decompress_br = entry_decompress_stream.reader(&.{}); | |
| 1592 | ||||||| 733b208256 | |
| 1593 | var current_entry: u32 = 0; | |
| 1594 | while (current_entry < pack_header.total_objects) : (current_entry += 1) { | |
| 1595 | const entry_offset = pack_counting_reader.bytes_read; | |
| 1596 | var entry_crc32_reader = hashedReader(pack_reader, std.hash.Crc32.init()); | |
| 1597 | const entry_header = try EntryHeader.read(format, entry_crc32_reader.reader()); | |
| 1598 | ======= | |
| 1599 | 1388 | for (0..pack_header.total_objects) |_| { |
| 1600 | 1389 | const entry_offset = pack.logicalPos() - pack_hashed.reader.bufferedLen(); |
| 1601 | 1390 | const entry_header = try EntryHeader.read(format, &pack_hashed.reader); |
| 1602 | >>>>>>> origin/flate | |
| 1603 | 1391 | switch (entry_header) { |
| 1604 | 1392 | .commit, .tree, .blob, .tag => |object| { |
| 1605 | <<<<<<< HEAD | |
| 1606 | var oid_hasher = Oid.Hasher.init(format); | |
| 1607 | var oid_hasher_buffer: [zlib.max_window_len]u8 = undefined; | |
| 1608 | var oid_hasher_bw = oid_hasher.writer(&oid_hasher_buffer); | |
| 1609 | ||||||| 733b208256 | |
| 1610 | var entry_decompress_stream = std.compress.zlib.decompressor(entry_crc32_reader.reader()); | |
| 1611 | var entry_counting_reader = std.io.countingReader(entry_decompress_stream.reader()); | |
| 1612 | var entry_hashed_writer = hashedWriter(std.io.null_writer, Oid.Hasher.init(format)); | |
| 1613 | const entry_writer = entry_hashed_writer.writer(); | |
| 1614 | ======= | |
| 1615 | 1393 | var entry_decompress: std.compress.flate.Decompress = .init(&pack_hashed.reader, .zlib, &.{}); |
| 1616 | 1394 | var oid_hasher: Oid.Hashing = .init(format, &flate_buffer); |
| 1617 | 1395 | const oid_hasher_w = oid_hasher.writer(); |
| 1618 | >>>>>>> origin/flate | |
| 1619 | 1396 | // The object header is not included in the pack data but is |
| 1620 | <<<<<<< HEAD | |
| 1621 | // part of the object's ID. | |
| 1622 | try oid_hasher_bw.print("{s} {d}\x00", .{ @tagName(entry_header), object.uncompressed_length }); | |
| 1623 | const n = try entry_decompress_br.readRemaining(&oid_hasher_bw); | |
| 1624 | if (n != object.uncompressed_length) return error.InvalidObject; | |
| 1625 | try oid_hasher_bw.flush(); | |
| 1626 | const oid = oid_hasher.finalResult(); | |
| 1627 | ||||||| 733b208256 | |
| 1628 | // part of the object's ID | |
| 1629 | try entry_writer.print("{s} {}\x00", .{ @tagName(entry_header), object.uncompressed_length }); | |
| 1630 | var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); | |
| 1631 | try fifo.pump(entry_counting_reader.reader(), entry_writer); | |
| 1632 | if (entry_counting_reader.bytes_read != object.uncompressed_length) { | |
| 1633 | return error.InvalidObject; | |
| 1634 | } | |
| 1635 | const oid = entry_hashed_writer.hasher.finalResult(); | |
| 1636 | ======= | |
| 1637 | 1397 | // part of the object's ID |
| 1638 | 1398 | try oid_hasher_w.print("{t} {d}\x00", .{ entry_header, object.uncompressed_length }); |
| 1639 | 1399 | const n = try entry_decompress.reader.streamRemaining(oid_hasher_w); |
| 1640 | 1400 | if (n != object.uncompressed_length) return error.InvalidObject; |
| 1641 | 1401 | const oid = oid_hasher.final(); |
| 1642 | 1402 | if (!skip_checksums) @compileError("TODO"); |
| 1643 | >>>>>>> origin/flate | |
| 1644 | 1403 | try index_entries.put(allocator, oid, .{ |
| 1645 | 1404 | .offset = entry_offset, |
| 1646 | 1405 | .crc32 = 0, |
| 1647 | 1406 | }); |
| 1648 | 1407 | }, |
| 1649 | 1408 | inline .ofs_delta, .ref_delta => |delta| { |
| 1650 | <<<<<<< HEAD | |
| 1651 | const n = try entry_decompress_br.discardRemaining(); | |
| 1652 | if (n != delta.uncompressed_length) return error.InvalidObject; | |
| 1653 | ||||||| 733b208256 | |
| 1654 | var entry_decompress_stream = std.compress.zlib.decompressor(entry_crc32_reader.reader()); | |
| 1655 | var entry_counting_reader = std.io.countingReader(entry_decompress_stream.reader()); | |
| 1656 | var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); | |
| 1657 | try fifo.pump(entry_counting_reader.reader(), std.io.null_writer); | |
| 1658 | if (entry_counting_reader.bytes_read != delta.uncompressed_length) { | |
| 1659 | return error.InvalidObject; | |
| 1660 | } | |
| 1661 | ======= | |
| 1662 | 1409 | var entry_decompress: std.compress.flate.Decompress = .init(&pack_hashed.reader, .zlib, &flate_buffer); |
| 1663 | 1410 | const n = try entry_decompress.reader.discardRemaining(); |
| 1664 | 1411 | if (n != delta.uncompressed_length) return error.InvalidObject; |
| 1665 | 1412 | if (!skip_checksums) @compileError("TODO"); |
| 1666 | >>>>>>> origin/flate | |
| 1667 | 1413 | try pending_deltas.append(allocator, .{ |
| 1668 | 1414 | .offset = entry_offset, |
| 1669 | 1415 | .crc32 = 0, |
| ... | ... | @@ -1672,28 +1418,8 @@ fn indexPackFirstPass( |
| 1672 | 1418 | } |
| 1673 | 1419 | } |
| 1674 | 1420 | |
| 1675 | <<<<<<< HEAD | |
| 1676 | const pack_checksum = pack_hashed.hasher.finalResult(); | |
| 1677 | const recorded_checksum = try Oid.readBytes(format, &pack.interface); | |
| 1678 | if (!mem.eql(u8, pack_checksum.slice(), recorded_checksum.slice())) { | |
| 1679 | return error.CorruptedPack; | |
| 1680 | } | |
| 1681 | return pack_checksum; | |
| 1682 | ||||||| 733b208256 | |
| 1683 | const pack_checksum = pack_hashed_reader.hasher.finalResult(); | |
| 1684 | const recorded_checksum = try Oid.readBytes(format, pack_buffered_reader.reader()); | |
| 1685 | if (!mem.eql(u8, pack_checksum.slice(), recorded_checksum.slice())) { | |
| 1686 | return error.CorruptedPack; | |
| 1687 | } | |
| 1688 | _ = pack_reader.readByte() catch |e| switch (e) { | |
| 1689 | error.EndOfStream => return pack_checksum, | |
| 1690 | else => |other| return other, | |
| 1691 | }; | |
| 1692 | return error.InvalidFormat; | |
| 1693 | ======= | |
| 1694 | 1421 | if (!skip_checksums) @compileError("TODO"); |
| 1695 | 1422 | return pack_hashed.hasher.finalResult(); |
| 1696 | >>>>>>> origin/flate | |
| 1697 | 1423 | } |
| 1698 | 1424 | |
| 1699 | 1425 | /// Attempts to determine the final object ID of the given deltified object. |
| ... | ... | @@ -1738,22 +1464,6 @@ fn indexPackHashDelta( |
| 1738 | 1464 | |
| 1739 | 1465 | const base_data = try resolveDeltaChain(allocator, format, pack, base_object, delta_offsets.items, cache); |
| 1740 | 1466 | |
| 1741 | <<<<<<< HEAD | |
| 1742 | var entry_hasher: Oid.Hasher = .init(format); | |
| 1743 | var entry_hasher_buffer: [64]u8 = undefined; | |
| 1744 | var entry_hasher_bw = entry_hasher.writer(&entry_hasher_buffer); | |
| 1745 | // Writes to hashers cannot fail. | |
| 1746 | entry_hasher_bw.print("{s} {d}\x00", .{ @tagName(base_object.type), base_data.len }) catch unreachable; | |
| 1747 | entry_hasher_bw.writeAll(base_data) catch unreachable; | |
| 1748 | entry_hasher_bw.flush() catch unreachable; | |
| 1749 | return entry_hasher.finalResult(); | |
| 1750 | ||||||| 733b208256 | |
| 1751 | var entry_hasher: Oid.Hasher = .init(format); | |
| 1752 | var entry_hashed_writer = hashedWriter(std.io.null_writer, &entry_hasher); | |
| 1753 | try entry_hashed_writer.writer().print("{s} {}\x00", .{ @tagName(base_object.type), base_data.len }); | |
| 1754 | entry_hasher.update(base_data); | |
| 1755 | return entry_hasher.finalResult(); | |
| 1756 | ======= | |
| 1757 | 1467 | var entry_hasher_buffer: [64]u8 = undefined; |
| 1758 | 1468 | var entry_hasher: Oid.Hashing = .init(format, &entry_hasher_buffer); |
| 1759 | 1469 | const entry_hasher_w = entry_hasher.writer(); |
| ... | ... | @@ -1761,7 +1471,6 @@ fn indexPackHashDelta( |
| 1761 | 1471 | entry_hasher_w.print("{t} {d}\x00", .{ base_object.type, base_data.len }) catch unreachable; |
| 1762 | 1472 | entry_hasher_w.writeAll(base_data) catch unreachable; |
| 1763 | 1473 | return entry_hasher.final(); |
| 1764 | >>>>>>> origin/flate | |
| 1765 | 1474 | } |
| 1766 | 1475 | |
| 1767 | 1476 | /// Resolves a chain of deltas, returning the final base object data. `pack` is |
| ... | ... | @@ -1783,24 +1492,6 @@ fn resolveDeltaChain( |
| 1783 | 1492 | |
| 1784 | 1493 | const delta_offset = delta_offsets[i]; |
| 1785 | 1494 | try pack.seekTo(delta_offset); |
| 1786 | <<<<<<< HEAD | |
| 1787 | const delta_header = try EntryHeader.read(format, &pack.interface); | |
| 1788 | _ = delta_header; | |
| 1789 | var delta_decompress: zlib.Decompressor = .init(&pack.interface); | |
| 1790 | var delta_decompress_buffer: [zlib.max_window_len]u8 = undefined; | |
| 1791 | var delta_reader = delta_decompress.reader(&delta_decompress_buffer); | |
| 1792 | _ = try readSizeVarInt(&delta_reader); // base object size | |
| 1793 | const expanded_size = try readSizeVarInt(&delta_reader); | |
| 1794 | ||||||| 733b208256 | |
| 1795 | const delta_header = try EntryHeader.read(format, pack.deprecatedReader()); | |
| 1796 | const delta_data = try readObjectRaw(allocator, pack.deprecatedReader(), delta_header.uncompressedLength()); | |
| 1797 | defer allocator.free(delta_data); | |
| 1798 | var delta_stream = std.io.fixedBufferStream(delta_data); | |
| 1799 | const delta_reader = delta_stream.reader(); | |
| 1800 | _ = try readSizeVarInt(delta_reader); // base object size | |
| 1801 | const expanded_size = try readSizeVarInt(delta_reader); | |
| 1802 | ||
| 1803 | ======= | |
| 1804 | 1495 | const delta_header = try EntryHeader.read(format, &pack.interface); |
| 1805 | 1496 | const delta_data = try readObjectRaw(allocator, &pack.interface, delta_header.uncompressedLength()); |
| 1806 | 1497 | defer allocator.free(delta_data); |
| ... | ... | @@ -1808,24 +1499,12 @@ fn resolveDeltaChain( |
| 1808 | 1499 | _ = try delta_reader.takeLeb128(u64); // base object size |
| 1809 | 1500 | const expanded_size = try delta_reader.takeLeb128(u64); |
| 1810 | 1501 | |
| 1811 | >>>>>>> origin/flate | |
| 1812 | 1502 | const expanded_alloc_size = std.math.cast(usize, expanded_size) orelse return error.ObjectTooLarge; |
| 1813 | 1503 | const expanded_data = try allocator.alloc(u8, expanded_alloc_size); |
| 1814 | 1504 | errdefer allocator.free(expanded_data); |
| 1815 | <<<<<<< HEAD | |
| 1816 | var expanded_delta_stream: Writer = .fixed(expanded_data); | |
| 1817 | try expandDelta(base_data, &delta_reader, &expanded_delta_stream); | |
| 1818 | if (expanded_delta_stream.end != expanded_size) return error.InvalidObject; | |
| 1819 | ||||||| 733b208256 | |
| 1820 | var expanded_delta_stream = std.io.fixedBufferStream(expanded_data); | |
| 1821 | var base_stream = std.io.fixedBufferStream(base_data); | |
| 1822 | try expandDelta(&base_stream, delta_reader, expanded_delta_stream.writer()); | |
| 1823 | if (expanded_delta_stream.pos != expanded_size) return error.InvalidObject; | |
| 1824 | ======= | |
| 1825 | 1505 | var expanded_delta_stream: std.Io.Writer = .fixed(expanded_data); |
| 1826 | 1506 | try expandDelta(base_data, &delta_reader, &expanded_delta_stream); |
| 1827 | 1507 | if (expanded_delta_stream.end != expanded_size) return error.InvalidObject; |
| 1828 | >>>>>>> origin/flate | |
| 1829 | 1508 | |
| 1830 | 1509 | try cache.put(allocator, delta_offset, .{ .type = base_object.type, .data = expanded_data }); |
| 1831 | 1510 | base_data = expanded_data; |
| ... | ... | @@ -1833,68 +1512,24 @@ fn resolveDeltaChain( |
| 1833 | 1512 | return base_data; |
| 1834 | 1513 | } |
| 1835 | 1514 | |
| 1836 | <<<<<<< HEAD | |
| 1837 | /// Reads the complete contents of an object from `reader`. | |
| 1838 | fn readObjectRaw(gpa: Allocator, reader: *std.io.Reader, size: u64) ![]u8 { | |
| 1839 | ||||||| 733b208256 | |
| 1840 | /// Reads the complete contents of an object from `reader`. This function may | |
| 1841 | /// read more bytes than required from `reader`, so the reader position after | |
| 1842 | /// returning is not reliable. | |
| 1843 | fn readObjectRaw(allocator: Allocator, reader: anytype, size: u64) ![]u8 { | |
| 1844 | ======= | |
| 1845 | 1515 | /// Reads the complete contents of an object from `reader`. This function may |
| 1846 | 1516 | /// read more bytes than required from `reader`, so the reader position after |
| 1847 | 1517 | /// returning is not reliable. |
| 1848 | 1518 | fn readObjectRaw(allocator: Allocator, reader: *std.Io.Reader, size: u64) ![]u8 { |
| 1849 | >>>>>>> origin/flate | |
| 1850 | 1519 | const alloc_size = std.math.cast(usize, size) orelse return error.ObjectTooLarge; |
| 1851 | <<<<<<< HEAD | |
| 1852 | var decompress: zlib.Decompressor = .init(reader); | |
| 1853 | var buffer: std.ArrayListUnmanaged(u8) = .empty; | |
| 1854 | defer buffer.deinit(gpa); | |
| 1855 | try decompress.reader().readRemainingArrayList(gpa, null, &buffer, .limited(alloc_size), zlib.max_window_len); | |
| 1856 | if (buffer.items.len < size) return error.EndOfStream; | |
| 1857 | return buffer.toOwnedSlice(gpa); | |
| 1858 | ||||||| 733b208256 | |
| 1859 | var buffered_reader = std.io.bufferedReader(reader); | |
| 1860 | var decompress_stream = std.compress.zlib.decompressor(buffered_reader.reader()); | |
| 1861 | const data = try allocator.alloc(u8, alloc_size); | |
| 1862 | errdefer allocator.free(data); | |
| 1863 | try decompress_stream.reader().readNoEof(data); | |
| 1864 | _ = decompress_stream.reader().readByte() catch |e| switch (e) { | |
| 1865 | error.EndOfStream => return data, | |
| 1866 | else => |other| return other, | |
| 1867 | }; | |
| 1868 | return error.InvalidFormat; | |
| 1869 | ======= | |
| 1870 | 1520 | var aw: std.Io.Writer.Allocating = .init(allocator); |
| 1871 | 1521 | try aw.ensureTotalCapacity(alloc_size + std.compress.flate.max_window_len); |
| 1872 | 1522 | defer aw.deinit(); |
| 1873 | 1523 | var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &.{}); |
| 1874 | 1524 | try decompress.reader.streamExact(&aw.writer, alloc_size); |
| 1875 | 1525 | return aw.toOwnedSlice(); |
| 1876 | >>>>>>> origin/flate | |
| 1877 | 1526 | } |
| 1878 | 1527 | |
| 1879 | <<<<<<< HEAD | |
| 1880 | ||||||| 733b208256 | |
| 1881 | /// Expands delta data from `delta_reader` to `writer`. `base_object` must | |
| 1882 | /// support `reader` and `seekTo` (such as a `std.io.FixedBufferStream`). | |
| 1883 | /// | |
| 1884 | ======= | |
| 1885 | 1528 | /// Expands delta data from `delta_reader` to `writer`. |
| 1886 | 1529 | /// |
| 1887 | >>>>>>> origin/flate | |
| 1888 | 1530 | /// The format of the delta data is documented in |
| 1889 | 1531 | /// [pack-format](https://git-scm.com/docs/pack-format). |
| 1890 | <<<<<<< HEAD | |
| 1891 | fn expandDelta(base_object: []const u8, delta_reader: *std.io.Reader, writer: *Writer) !void { | |
| 1892 | var base_offset: u32 = 0; | |
| 1893 | ||||||| 733b208256 | |
| 1894 | fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !void { | |
| 1895 | ======= | |
| 1896 | 1532 | fn expandDelta(base_object: []const u8, delta_reader: *std.Io.Reader, writer: *std.Io.Writer) !void { |
| 1897 | >>>>>>> origin/flate | |
| 1898 | 1533 | while (true) { |
| 1899 | 1534 | const inst: packed struct { value: u7, copy: bool } = @bitCast(delta_reader.takeByte() catch |e| switch (e) { |
| 1900 | 1535 | error.EndOfStream => return, |
| ... | ... | @@ -1916,13 +1551,7 @@ fn expandDelta(base_object: []const u8, delta_reader: *std.Io.Reader, writer: *s |
| 1916 | 1551 | .offset3 = if (available.offset3) try delta_reader.takeByte() else 0, |
| 1917 | 1552 | .offset4 = if (available.offset4) try delta_reader.takeByte() else 0, |
| 1918 | 1553 | }; |
| 1919 | <<<<<<< HEAD | |
| 1920 | base_offset = @bitCast(offset_parts); | |
| 1921 | ||||||| 733b208256 | |
| 1922 | const offset: u32 = @bitCast(offset_parts); | |
| 1923 | ======= | |
| 1924 | 1554 | const base_offset: u32 = @bitCast(offset_parts); |
| 1925 | >>>>>>> origin/flate | |
| 1926 | 1555 | const size_parts: packed struct { size1: u8, size2: u8, size3: u8 } = .{ |
| 1927 | 1556 | .size1 = if (available.size1) try delta_reader.takeByte() else 0, |
| 1928 | 1557 | .size2 = if (available.size2) try delta_reader.takeByte() else 0, |
| ... | ... | @@ -1930,28 +1559,9 @@ fn expandDelta(base_object: []const u8, delta_reader: *std.Io.Reader, writer: *s |
| 1930 | 1559 | }; |
| 1931 | 1560 | var size: u24 = @bitCast(size_parts); |
| 1932 | 1561 | if (size == 0) size = 0x10000; |
| 1933 | <<<<<<< HEAD | |
| 1934 | ||
| 1935 | 1562 | try writer.writeAll(base_object[base_offset..][0..size]); |
| 1936 | base_offset += size; | |
| 1937 | ||||||| 733b208256 | |
| 1938 | try base_object.seekTo(offset); | |
| 1939 | var copy_reader = std.io.limitedReader(base_object.reader(), size); | |
| 1940 | var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); | |
| 1941 | try fifo.pump(copy_reader.reader(), writer); | |
| 1942 | ======= | |
| 1943 | try writer.writeAll(base_object[base_offset..][0..size]); | |
| 1944 | >>>>>>> origin/flate | |
| 1945 | 1563 | } else if (inst.value != 0) { |
| 1946 | <<<<<<< HEAD | |
| 1947 | try delta_reader.readAll(writer, .limited(inst.value)); | |
| 1948 | ||||||| 733b208256 | |
| 1949 | var data_reader = std.io.limitedReader(delta_reader, inst.value); | |
| 1950 | var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init(); | |
| 1951 | try fifo.pump(data_reader.reader(), writer); | |
| 1952 | ======= | |
| 1953 | 1564 | try delta_reader.streamExact(writer, inst.value); |
| 1954 | >>>>>>> origin/flate | |
| 1955 | 1565 | } else { |
| 1956 | 1566 | return error.InvalidDeltaInstruction; |
| 1957 | 1567 | } |
| ... | ... | @@ -1986,16 +1596,9 @@ fn runRepositoryTest(comptime format: Oid.Format, head_commit: []const u8) !void |
| 1986 | 1596 | |
| 1987 | 1597 | var index_file = try git_dir.dir.createFile("testrepo.idx", .{ .read = true }); |
| 1988 | 1598 | defer index_file.close(); |
| 1989 | <<<<<<< HEAD | |
| 1990 | var index_file_writer = index_file.writer(); | |
| 1991 | try indexPack(testing.allocator, format, pack_file, &index_file_writer); | |
| 1992 | ||||||| 733b208256 | |
| 1993 | try indexPack(testing.allocator, format, pack_file, index_file.deprecatedWriter()); | |
| 1994 | ======= | |
| 1995 | 1599 | var index_file_buffer: [2000]u8 = undefined; |
| 1996 | 1600 | var index_file_writer = index_file.writer(&index_file_buffer); |
| 1997 | 1601 | try indexPack(testing.allocator, format, &pack_file_reader, &index_file_writer); |
| 1998 | >>>>>>> origin/flate | |
| 1999 | 1602 | |
| 2000 | 1603 | // Arbitrary size limit on files read while checking the repository contents |
| 2001 | 1604 | // (all files in the test repo are known to be smaller than this) |
| ... | ... | @@ -2011,16 +1614,9 @@ fn runRepositoryTest(comptime format: Oid.Format, head_commit: []const u8) !void |
| 2011 | 1614 | try testing.expectEqualSlices(u8, testrepo_idx, index_file_data); |
| 2012 | 1615 | } |
| 2013 | 1616 | |
| 2014 | <<<<<<< HEAD | |
| 2015 | var index_file_reader = index_file_writer.moveToReader(); | |
| 2016 | var repository = try Repository.init(testing.allocator, format, pack_file, &index_file_reader); | |
| 2017 | ||||||| 733b208256 | |
| 2018 | var repository = try Repository.init(testing.allocator, format, pack_file, index_file); | |
| 2019 | ======= | |
| 2020 | 1617 | var index_file_reader = index_file.reader(&index_file_buffer); |
| 2021 | 1618 | var repository: Repository = undefined; |
| 2022 | 1619 | try repository.init(testing.allocator, format, &pack_file_reader, &index_file_reader); |
| 2023 | >>>>>>> origin/flate | |
| 2024 | 1620 | defer repository.deinit(); |
| 2025 | 1621 | |
| 2026 | 1622 | var worktree = testing.tmpDir(.{ .iterate = true }); |
| ... | ... | @@ -2107,12 +1703,10 @@ test "SHA-256 packfile indexing and checkout" { |
| 2107 | 1703 | /// Checks out a commit of a packfile. Intended for experimenting with and |
| 2108 | 1704 | /// benchmarking possible optimizations to the indexing and checkout behavior. |
| 2109 | 1705 | pub fn main() !void { |
| 2110 | var debug_allocator: std.heap.DebugAllocator(.{}) = .init; | |
| 2111 | defer _ = debug_allocator.deinit(); | |
| 2112 | const gpa = if (std.debug.runtime_safety) debug_allocator.allocator() else std.heap.smp_allocator; | |
| 1706 | const allocator = std.heap.smp_allocator; | |
| 2113 | 1707 | |
| 2114 | const args = try std.process.argsAlloc(gpa); | |
| 2115 | defer std.process.argsFree(gpa, args); | |
| 1708 | const args = try std.process.argsAlloc(allocator); | |
| 1709 | defer std.process.argsFree(allocator, args); | |
| 2116 | 1710 | if (args.len != 5) { |
| 2117 | 1711 | return error.InvalidArguments; // Arguments: format packfile commit worktree |
| 2118 | 1712 | } |
| ... | ... | @@ -2134,34 +1728,16 @@ pub fn main() !void { |
| 2134 | 1728 | std.debug.print("Starting index...\n", .{}); |
| 2135 | 1729 | var index_file = try git_dir.createFile("idx", .{ .read = true }); |
| 2136 | 1730 | defer index_file.close(); |
| 2137 | <<<<<<< HEAD | |
| 2138 | var index_file_writer = index_file.writer(); | |
| 2139 | var pack_file_reader = pack_file.reader(); | |
| 2140 | try indexPack(gpa, format, &pack_file_reader, &index_file_writer); | |
| 2141 | try index_file.sync(); | |
| 2142 | ||||||| 733b208256 | |
| 2143 | var index_buffered_writer = std.io.bufferedWriter(index_file.deprecatedWriter()); | |
| 2144 | try indexPack(allocator, format, pack_file, index_buffered_writer.writer()); | |
| 2145 | try index_buffered_writer.flush(); | |
| 2146 | try index_file.sync(); | |
| 2147 | ======= | |
| 2148 | var index_buffered_writer = std.io.bufferedWriter(index_file.deprecatedWriter()); | |
| 2149 | try indexPack(allocator, format, &pack_file_reader, index_buffered_writer.writer()); | |
| 2150 | try index_buffered_writer.flush(); | |
| 2151 | >>>>>>> origin/flate | |
| 1731 | var index_file_buffer: [4096]u8 = undefined; | |
| 1732 | var index_file_writer = index_file.writer(&index_file_buffer); | |
| 1733 | try indexPack(allocator, format, &pack_file_reader, &index_file_writer); | |
| 2152 | 1734 | |
| 2153 | 1735 | std.debug.print("Starting checkout...\n", .{}); |
| 2154 | <<<<<<< HEAD | |
| 2155 | var index_file_reader = index_file_writer.moveToReader(); | |
| 1736 | var index_file_reader = index_file.reader(&index_file_buffer); | |
| 2156 | 1737 | var repository: Repository = undefined; |
| 2157 | try repository.init(gpa, format, &pack_file_reader, &index_file_reader); | |
| 2158 | ||||||| 733b208256 | |
| 2159 | var repository = try Repository.init(allocator, format, pack_file, index_file); | |
| 2160 | ======= | |
| 2161 | var repository = try Repository.init(allocator, format, &pack_file_reader, index_file); | |
| 2162 | >>>>>>> origin/flate | |
| 1738 | try repository.init(allocator, format, &pack_file_reader, &index_file_reader); | |
| 2163 | 1739 | defer repository.deinit(); |
| 2164 | var diagnostics: Diagnostics = .{ .allocator = gpa }; | |
| 1740 | var diagnostics: Diagnostics = .{ .allocator = allocator }; | |
| 2165 | 1741 | defer diagnostics.deinit(); |
| 2166 | 1742 | try repository.checkout(worktree, commit, &diagnostics); |
| 2167 | 1743 |
src/Sema.zig+60-73| ... | ... | @@ -2631,7 +2631,7 @@ fn reparentOwnedErrorMsg( |
| 2631 | 2631 | |
| 2632 | 2632 | const orig_notes = msg.notes.len; |
| 2633 | 2633 | msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); |
| 2634 | std.mem.copyBackwards(Zcu.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]); | |
| 2634 | @memmove(msg.notes[1..][0..orig_notes], msg.notes[0..orig_notes]); | |
| 2635 | 2635 | msg.notes[0] = .{ |
| 2636 | 2636 | .src_loc = msg.src_loc, |
| 2637 | 2637 | .msg = msg.msg, |
| ... | ... | @@ -2649,7 +2649,13 @@ pub fn analyzeAsAlign( |
| 2649 | 2649 | src: LazySrcLoc, |
| 2650 | 2650 | air_ref: Air.Inst.Ref, |
| 2651 | 2651 | ) !Alignment { |
| 2652 | const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, .{ .simple = .@"align" }); | |
| 2652 | const alignment_big = try sema.analyzeAsInt( | |
| 2653 | block, | |
| 2654 | src, | |
| 2655 | air_ref, | |
| 2656 | align_ty, | |
| 2657 | .{ .simple = .@"align" }, | |
| 2658 | ); | |
| 2653 | 2659 | return sema.validateAlign(block, src, alignment_big); |
| 2654 | 2660 | } |
| 2655 | 2661 | |
| ... | ... | @@ -3926,11 +3932,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, |
| 3926 | 3932 | // Whilst constructing our mapping, we will also initialize optional and error union payloads when |
| 3927 | 3933 | // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters. |
| 3928 | 3934 | var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len); |
| 3935 | ||
| 3929 | 3936 | for (stores) |store_inst_idx| { |
| 3930 | 3937 | const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx)); |
| 3931 | 3938 | const ptr_to_map = switch (store_inst.tag) { |
| 3932 | 3939 | .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to. |
| 3933 | .set_union_tag => continue, // Ignore for now; handled after we map pointers | |
| 3940 | .set_union_tag => store_inst.data.bin_op.lhs.toIndex().?, // Map the union pointer. | |
| 3934 | 3941 | .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself. |
| 3935 | 3942 | else => unreachable, |
| 3936 | 3943 | }; |
| ... | ... | @@ -4047,13 +4054,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, |
| 4047 | 4054 | const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); |
| 4048 | 4055 | if (zcu.typeToUnion(maybe_union_ty)) |union_obj| { |
| 4049 | 4056 | // As this is a union field, we must store to the pointer now to set the tag. |
| 4050 | // If the payload is OPV, there will not be a payload store, so we store that value. | |
| 4051 | // Otherwise, there will be a payload store to process later, so undef will suffice. | |
| 4057 | // The payload value will be stored later, so undef is a sufficent payload for now. | |
| 4052 | 4058 | const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]); |
| 4053 | const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty); | |
| 4059 | const payload_val = try pt.undefValue(payload_ty); | |
| 4054 | 4060 | const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx); |
| 4055 | 4061 | const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val); |
| 4056 | try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), store_val, maybe_union_ty); | |
| 4062 | try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty); | |
| 4057 | 4063 | } |
| 4058 | 4064 | break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern(); |
| 4059 | 4065 | }, |
| ... | ... | @@ -8900,6 +8906,14 @@ fn resolveGenericBody( |
| 8900 | 8906 | return sema.resolveConstDefinedValue(block, src, result, reason); |
| 8901 | 8907 | } |
| 8902 | 8908 | |
| 8909 | /// Given a library name, examines if the library name should end up in | |
| 8910 | /// `link.File.Options.windows_libs` table (for example, libc is always | |
| 8911 | /// specified via dedicated flag `link_libc` instead), | |
| 8912 | /// and puts it there if it doesn't exist. | |
| 8913 | /// It also dupes the library name which can then be saved as part of the | |
| 8914 | /// respective `Decl` (either `ExternFn` or `Var`). | |
| 8915 | /// The liveness of the duped library name is tied to liveness of `Zcu`. | |
| 8916 | /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). | |
| 8903 | 8917 | pub fn handleExternLibName( |
| 8904 | 8918 | sema: *Sema, |
| 8905 | 8919 | block: *Block, |
| ... | ... | @@ -8949,6 +8963,11 @@ pub fn handleExternLibName( |
| 8949 | 8963 | .{ lib_name, lib_name }, |
| 8950 | 8964 | ); |
| 8951 | 8965 | } |
| 8966 | comp.addLinkLib(lib_name) catch |err| { | |
| 8967 | return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ | |
| 8968 | lib_name, @errorName(err), | |
| 8969 | }); | |
| 8970 | }; | |
| 8952 | 8971 | } |
| 8953 | 8972 | } |
| 8954 | 8973 | |
| ... | ... | @@ -14458,8 +14477,8 @@ fn analyzeTupleMul( |
| 14458 | 14477 | } |
| 14459 | 14478 | } |
| 14460 | 14479 | for (0..factor) |i| { |
| 14461 | mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]); | |
| 14462 | mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]); | |
| 14480 | @memmove(types[tuple_len * i ..][0..tuple_len], types[0..tuple_len]); | |
| 14481 | @memmove(values[tuple_len * i ..][0..tuple_len], values[0..tuple_len]); | |
| 14463 | 14482 | } |
| 14464 | 14483 | break :rs runtime_src; |
| 14465 | 14484 | }; |
| ... | ... | @@ -18817,7 +18836,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 18817 | 18836 | const abi_align: Alignment = if (inst_data.flags.has_align) blk: { |
| 18818 | 18837 | const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]); |
| 18819 | 18838 | extra_i += 1; |
| 18820 | const coerced = try sema.coerce(block, .u32, try sema.resolveInst(ref), align_src); | |
| 18839 | const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src); | |
| 18821 | 18840 | const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" }); |
| 18822 | 18841 | // Check if this happens to be the lazy alignment of our element type, in |
| 18823 | 18842 | // which case we can make this 0 without resolving it. |
| ... | ... | @@ -20335,15 +20354,11 @@ fn zirReify( |
| 20335 | 20354 | try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls), |
| 20336 | 20355 | ).?); |
| 20337 | 20356 | |
| 20338 | if (!try sema.intFitsInType(alignment_val, .u32, null)) { | |
| 20339 | return sema.fail(block, src, "alignment must fit in 'u32'", .{}); | |
| 20357 | if (!try sema.intFitsInType(alignment_val, align_ty, null)) { | |
| 20358 | return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); | |
| 20340 | 20359 | } |
| 20341 | ||
| 20342 | 20360 | const alignment_val_int = try alignment_val.toUnsignedIntSema(pt); |
| 20343 | if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) { | |
| 20344 | return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{alignment_val_int}); | |
| 20345 | } | |
| 20346 | const abi_align = Alignment.fromByteUnits(alignment_val_int); | |
| 20361 | const abi_align = try sema.validateAlign(block, src, alignment_val_int); | |
| 20347 | 20362 | |
| 20348 | 20363 | const elem_ty = child_val.toType(); |
| 20349 | 20364 | if (abi_align != .none) { |
| ... | ... | @@ -20920,8 +20935,6 @@ fn reifyUnion( |
| 20920 | 20935 | std.hash.autoHash(&hasher, opt_tag_type_val.toIntern()); |
| 20921 | 20936 | std.hash.autoHash(&hasher, fields_len); |
| 20922 | 20937 | |
| 20923 | var any_aligns = false; | |
| 20924 | ||
| 20925 | 20938 | for (0..fields_len) |field_idx| { |
| 20926 | 20939 | const field_info = try fields_val.elemValue(pt, field_idx); |
| 20927 | 20940 | |
| ... | ... | @@ -20930,16 +20943,11 @@ fn reifyUnion( |
| 20930 | 20943 | const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 2)); |
| 20931 | 20944 | |
| 20932 | 20945 | const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .union_field_name }); |
| 20933 | ||
| 20934 | 20946 | std.hash.autoHash(&hasher, .{ |
| 20935 | 20947 | field_name, |
| 20936 | 20948 | field_type_val.toIntern(), |
| 20937 | 20949 | field_align_val.toIntern(), |
| 20938 | 20950 | }); |
| 20939 | ||
| 20940 | if (field_align_val.toUnsignedInt(zcu) != 0) { | |
| 20941 | any_aligns = true; | |
| 20942 | } | |
| 20943 | 20951 | } |
| 20944 | 20952 | |
| 20945 | 20953 | const tracked_inst = try block.trackZir(inst); |
| ... | ... | @@ -20956,7 +20964,7 @@ fn reifyUnion( |
| 20956 | 20964 | true => .safety, |
| 20957 | 20965 | false => .none, |
| 20958 | 20966 | }, |
| 20959 | .any_aligned_fields = any_aligns, | |
| 20967 | .any_aligned_fields = layout != .@"packed", | |
| 20960 | 20968 | .requires_comptime = .unknown, |
| 20961 | 20969 | .assumed_runtime_bits = false, |
| 20962 | 20970 | .assumed_pointer_aligned = false, |
| ... | ... | @@ -20989,8 +20997,7 @@ fn reifyUnion( |
| 20989 | 20997 | ); |
| 20990 | 20998 | wip_ty.setName(ip, type_name.name, type_name.nav); |
| 20991 | 20999 | |
| 20992 | const field_types = try sema.arena.alloc(InternPool.Index, fields_len); | |
| 20993 | const field_aligns = if (any_aligns) try sema.arena.alloc(InternPool.Alignment, fields_len) else undefined; | |
| 21000 | const loaded_union = ip.loadUnionType(wip_ty.index); | |
| 20994 | 21001 | |
| 20995 | 21002 | const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(zcu)) |tag_type_val| tag_ty: { |
| 20996 | 21003 | switch (ip.indexToKey(tag_type_val.toIntern())) { |
| ... | ... | @@ -21003,11 +21010,12 @@ fn reifyUnion( |
| 21003 | 21010 | const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu); |
| 21004 | 21011 | var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len); |
| 21005 | 21012 | |
| 21006 | for (field_types, 0..) |*field_ty, field_idx| { | |
| 21013 | for (0..fields_len) |field_idx| { | |
| 21007 | 21014 | const field_info = try fields_val.elemValue(pt, field_idx); |
| 21008 | 21015 | |
| 21009 | 21016 | const field_name_val = try field_info.fieldValue(pt, 0); |
| 21010 | 21017 | const field_type_val = try field_info.fieldValue(pt, 1); |
| 21018 | const field_alignment_val = try field_info.fieldValue(pt, 2); | |
| 21011 | 21019 | |
| 21012 | 21020 | // Don't pass a reason; first loop acts as an assertion that this is valid. |
| 21013 | 21021 | const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); |
| ... | ... | @@ -21024,14 +21032,12 @@ fn reifyUnion( |
| 21024 | 21032 | } |
| 21025 | 21033 | seen_tags.set(enum_index); |
| 21026 | 21034 | |
| 21027 | field_ty.* = field_type_val.toIntern(); | |
| 21028 | if (any_aligns) { | |
| 21029 | const byte_align = try (try field_info.fieldValue(pt, 2)).toUnsignedIntSema(pt); | |
| 21030 | if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) { | |
| 21031 | // TODO: better source location | |
| 21032 | return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); | |
| 21033 | } | |
| 21034 | field_aligns[field_idx] = Alignment.fromByteUnits(byte_align); | |
| 21035 | loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); | |
| 21036 | const byte_align = try field_alignment_val.toUnsignedIntSema(pt); | |
| 21037 | if (layout == .@"packed") { | |
| 21038 | if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); | |
| 21039 | } else { | |
| 21040 | loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); | |
| 21035 | 21041 | } |
| 21036 | 21042 | } |
| 21037 | 21043 | |
| ... | ... | @@ -21055,11 +21061,12 @@ fn reifyUnion( |
| 21055 | 21061 | var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; |
| 21056 | 21062 | try field_names.ensureTotalCapacity(sema.arena, fields_len); |
| 21057 | 21063 | |
| 21058 | for (field_types, 0..) |*field_ty, field_idx| { | |
| 21064 | for (0..fields_len) |field_idx| { | |
| 21059 | 21065 | const field_info = try fields_val.elemValue(pt, field_idx); |
| 21060 | 21066 | |
| 21061 | 21067 | const field_name_val = try field_info.fieldValue(pt, 0); |
| 21062 | 21068 | const field_type_val = try field_info.fieldValue(pt, 1); |
| 21069 | const field_alignment_val = try field_info.fieldValue(pt, 2); | |
| 21063 | 21070 | |
| 21064 | 21071 | // Don't pass a reason; first loop acts as an assertion that this is valid. |
| 21065 | 21072 | const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined); |
| ... | ... | @@ -21069,14 +21076,12 @@ fn reifyUnion( |
| 21069 | 21076 | return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)}); |
| 21070 | 21077 | } |
| 21071 | 21078 | |
| 21072 | field_ty.* = field_type_val.toIntern(); | |
| 21073 | if (any_aligns) { | |
| 21074 | const byte_align = try (try field_info.fieldValue(pt, 2)).toUnsignedIntSema(pt); | |
| 21075 | if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) { | |
| 21076 | // TODO: better source location | |
| 21077 | return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); | |
| 21078 | } | |
| 21079 | field_aligns[field_idx] = Alignment.fromByteUnits(byte_align); | |
| 21079 | loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern(); | |
| 21080 | const byte_align = try field_alignment_val.toUnsignedIntSema(pt); | |
| 21081 | if (layout == .@"packed") { | |
| 21082 | if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{}); | |
| 21083 | } else { | |
| 21084 | loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); | |
| 21080 | 21085 | } |
| 21081 | 21086 | } |
| 21082 | 21087 | |
| ... | ... | @@ -21085,7 +21090,7 @@ fn reifyUnion( |
| 21085 | 21090 | }; |
| 21086 | 21091 | errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error |
| 21087 | 21092 | |
| 21088 | for (field_types) |field_ty_ip| { | |
| 21093 | for (loaded_union.field_types.get(ip)) |field_ty_ip| { | |
| 21089 | 21094 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 21090 | 21095 | if (field_ty.zigTypeTag(zcu) == .@"opaque") { |
| 21091 | 21096 | return sema.failWithOwnedErrorMsg(block, msg: { |
| ... | ... | @@ -21119,11 +21124,6 @@ fn reifyUnion( |
| 21119 | 21124 | } |
| 21120 | 21125 | } |
| 21121 | 21126 | |
| 21122 | const loaded_union = ip.loadUnionType(wip_ty.index); | |
| 21123 | loaded_union.setFieldTypes(ip, field_types); | |
| 21124 | if (any_aligns) { | |
| 21125 | loaded_union.setFieldAligns(ip, field_aligns); | |
| 21126 | } | |
| 21127 | 21127 | loaded_union.setTagType(ip, enum_tag_ty); |
| 21128 | 21128 | loaded_union.setStatus(ip, .have_field_types); |
| 21129 | 21129 | |
| ... | ... | @@ -21276,7 +21276,6 @@ fn reifyStruct( |
| 21276 | 21276 | |
| 21277 | 21277 | var any_comptime_fields = false; |
| 21278 | 21278 | var any_default_inits = false; |
| 21279 | var any_aligned_fields = false; | |
| 21280 | 21279 | |
| 21281 | 21280 | for (0..fields_len) |field_idx| { |
| 21282 | 21281 | const field_info = try fields_val.elemValue(pt, field_idx); |
| ... | ... | @@ -21311,11 +21310,6 @@ fn reifyStruct( |
| 21311 | 21310 | |
| 21312 | 21311 | if (field_is_comptime) any_comptime_fields = true; |
| 21313 | 21312 | if (field_default_value != .none) any_default_inits = true; |
| 21314 | switch (try field_alignment_val.orderAgainstZeroSema(pt)) { | |
| 21315 | .eq => {}, | |
| 21316 | .gt => any_aligned_fields = true, | |
| 21317 | .lt => unreachable, | |
| 21318 | } | |
| 21319 | 21313 | } |
| 21320 | 21314 | |
| 21321 | 21315 | const tracked_inst = try block.trackZir(inst); |
| ... | ... | @@ -21327,7 +21321,7 @@ fn reifyStruct( |
| 21327 | 21321 | .requires_comptime = .unknown, |
| 21328 | 21322 | .any_comptime_fields = any_comptime_fields, |
| 21329 | 21323 | .any_default_inits = any_default_inits, |
| 21330 | .any_aligned_fields = any_aligned_fields, | |
| 21324 | .any_aligned_fields = layout != .@"packed", | |
| 21331 | 21325 | .inits_resolved = true, |
| 21332 | 21326 | .key = .{ .reified = .{ |
| 21333 | 21327 | .zir_index = tracked_inst, |
| ... | ... | @@ -21371,21 +21365,14 @@ fn reifyStruct( |
| 21371 | 21365 | return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)}); |
| 21372 | 21366 | } |
| 21373 | 21367 | |
| 21374 | if (any_aligned_fields) { | |
| 21375 | if (!try sema.intFitsInType(field_alignment_val, .u32, null)) { | |
| 21376 | return sema.fail(block, src, "alignment must fit in 'u32'", .{}); | |
| 21377 | } | |
| 21378 | ||
| 21379 | const byte_align = try field_alignment_val.toUnsignedIntSema(pt); | |
| 21380 | if (byte_align == 0) { | |
| 21381 | if (layout != .@"packed") { | |
| 21382 | struct_type.field_aligns.get(ip)[field_idx] = .none; | |
| 21383 | } | |
| 21384 | } else { | |
| 21385 | if (layout == .@"packed") return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); | |
| 21386 | if (!math.isPowerOfTwo(byte_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); | |
| 21387 | struct_type.field_aligns.get(ip)[field_idx] = Alignment.fromNonzeroByteUnits(byte_align); | |
| 21388 | } | |
| 21368 | if (!try sema.intFitsInType(field_alignment_val, align_ty, null)) { | |
| 21369 | return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)}); | |
| 21370 | } | |
| 21371 | const byte_align = try field_alignment_val.toUnsignedIntSema(pt); | |
| 21372 | if (layout == .@"packed") { | |
| 21373 | if (byte_align != 0) return sema.fail(block, src, "alignment of a packed struct field must be set to 0", .{}); | |
| 21374 | } else { | |
| 21375 | struct_type.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align); | |
| 21389 | 21376 | } |
| 21390 | 21377 | |
| 21391 | 21378 | const field_is_comptime = field_is_comptime_val.toBool(); |
src/Zcu.zig+9-4| ... | ... | @@ -268,7 +268,8 @@ nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, voi |
| 268 | 268 | |
| 269 | 269 | /// These are the modules which we initially queue for analysis in `Compilation.update`. |
| 270 | 270 | /// `resolveReferences` will use these as the root of its reachability traversal. |
| 271 | analysis_roots: std.BoundedArray(*Package.Module, 4) = .{}, | |
| 271 | analysis_roots_buffer: [4]*Package.Module, | |
| 272 | analysis_roots_len: usize = 0, | |
| 272 | 273 | /// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and |
| 273 | 274 | /// reset to `null` when any semantic analysis occurs (since this invalidates the data). |
| 274 | 275 | /// Allocated into `gpa`. |
| ... | ... | @@ -4013,8 +4014,8 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv |
| 4013 | 4014 | // This is not a sufficient size, but a lower bound. |
| 4014 | 4015 | try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count())); |
| 4015 | 4016 | |
| 4016 | try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots.len); | |
| 4017 | for (zcu.analysis_roots.slice()) |mod| { | |
| 4017 | try type_queue.ensureTotalCapacity(gpa, zcu.analysis_roots_len); | |
| 4018 | for (zcu.analysisRoots()) |mod| { | |
| 4018 | 4019 | const file = zcu.module_roots.get(mod).?.unwrap() orelse continue; |
| 4019 | 4020 | const root_ty = zcu.fileRootType(file); |
| 4020 | 4021 | if (root_ty == .none) continue; |
| ... | ... | @@ -4202,6 +4203,10 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv |
| 4202 | 4203 | return result; |
| 4203 | 4204 | } |
| 4204 | 4205 | |
| 4206 | pub fn analysisRoots(zcu: *Zcu) []*Package.Module { | |
| 4207 | return zcu.analysis_roots_buffer[0..zcu.analysis_roots_len]; | |
| 4208 | } | |
| 4209 | ||
| 4205 | 4210 | pub fn fileByIndex(zcu: *const Zcu, file_index: File.Index) *File { |
| 4206 | 4211 | return zcu.intern_pool.filePtr(file_index); |
| 4207 | 4212 | } |
| ... | ... | @@ -4510,7 +4515,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu |
| 4510 | 4515 | }, |
| 4511 | 4516 | .stage2_spirv => switch (cc) { |
| 4512 | 4517 | .spirv_device, .spirv_kernel => true, |
| 4513 | .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan, | |
| 4518 | .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl, | |
| 4514 | 4519 | else => false, |
| 4515 | 4520 | }, |
| 4516 | 4521 | }; |
src/Zcu/PerThread.zig+3-2| ... | ... | @@ -2116,8 +2116,9 @@ pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool { |
| 2116 | 2116 | // multi-threaded environment (where things like file indices could differ between compiler runs). |
| 2117 | 2117 | |
| 2118 | 2118 | // The roots of our file liveness analysis will be the analysis roots. |
| 2119 | try zcu.alive_files.ensureTotalCapacity(gpa, zcu.analysis_roots.len); | |
| 2120 | for (zcu.analysis_roots.slice()) |mod| { | |
| 2119 | const analysis_roots = zcu.analysisRoots(); | |
| 2120 | try zcu.alive_files.ensureTotalCapacity(gpa, analysis_roots.len); | |
| 2121 | for (analysis_roots) |mod| { | |
| 2121 | 2122 | const file_index = zcu.module_roots.get(mod).?.unwrap() orelse continue; |
| 2122 | 2123 | const file = zcu.fileByIndex(file_index); |
| 2123 | 2124 |
src/codegen/aarch64/Assemble.zig+10-7| ... | ... | @@ -33,13 +33,16 @@ pub fn nextInstruction(as: *Assemble) !?Instruction { |
| 33 | 33 | var symbols: Symbols: { |
| 34 | 34 | const symbols = @typeInfo(@TypeOf(instruction.symbols)).@"struct".fields; |
| 35 | 35 | var symbol_fields: [symbols.len]std.builtin.Type.StructField = undefined; |
| 36 | for (&symbol_fields, symbols) |*symbol_field, symbol| symbol_field.* = .{ | |
| 37 | .name = symbol.name, | |
| 38 | .type = zonCast(SymbolSpec, @field(instruction.symbols, symbol.name), .{}).Storage(), | |
| 39 | .default_value_ptr = null, | |
| 40 | .is_comptime = false, | |
| 41 | .alignment = 0, | |
| 42 | }; | |
| 36 | for (&symbol_fields, symbols) |*symbol_field, symbol| { | |
| 37 | const Storage = zonCast(SymbolSpec, @field(instruction.symbols, symbol.name), .{}).Storage(); | |
| 38 | symbol_field.* = .{ | |
| 39 | .name = symbol.name, | |
| 40 | .type = Storage, | |
| 41 | .default_value_ptr = null, | |
| 42 | .is_comptime = false, | |
| 43 | .alignment = @alignOf(Storage), | |
| 44 | }; | |
| 45 | } | |
| 43 | 46 | break :Symbols @Type(.{ .@"struct" = .{ |
| 44 | 47 | .layout = .auto, |
| 45 | 48 | .fields = &symbol_fields, |
src/codegen/llvm.zig+7-4| ... | ... | @@ -12210,11 +12210,14 @@ fn lowerFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) |
| 12210 | 12210 | }, |
| 12211 | 12211 | .riscv64_lp64, .riscv32_ilp32 => switch (riscv_c_abi.classifyType(return_type, zcu)) { |
| 12212 | 12212 | .memory => return .void, |
| 12213 | .integer => { | |
| 12214 | return o.builder.intType(@intCast(return_type.bitSize(zcu))); | |
| 12215 | }, | |
| 12213 | .integer => return o.builder.intType(@intCast(return_type.bitSize(zcu))), | |
| 12216 | 12214 | .double_integer => { |
| 12217 | return o.builder.structType(.normal, &.{ .i64, .i64 }); | |
| 12215 | const integer: Builder.Type = switch (zcu.getTarget().cpu.arch) { | |
| 12216 | .riscv64 => .i64, | |
| 12217 | .riscv32 => .i32, | |
| 12218 | else => unreachable, | |
| 12219 | }; | |
| 12220 | return o.builder.structType(.normal, &.{ integer, integer }); | |
| 12218 | 12221 | }, |
| 12219 | 12222 | .byval => return o.lowerType(pt, return_type), |
| 12220 | 12223 | .fields => { |
src/libs/freebsd.zig-4| ... | ... | @@ -977,10 +977,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 977 | 977 | }); |
| 978 | 978 | } |
| 979 | 979 | |
| 980 | pub fn sharedObjectsCount() u8 { | |
| 981 | return libs.len; | |
| 982 | } | |
| 983 | ||
| 984 | 980 | fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { |
| 985 | 981 | assert(comp.freebsd_so_files == null); |
| 986 | 982 | comp.freebsd_so_files = so_files; |
src/libs/glibc.zig-12| ... | ... | @@ -1120,18 +1120,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 1120 | 1120 | }); |
| 1121 | 1121 | } |
| 1122 | 1122 | |
| 1123 | pub fn sharedObjectsCount(target: *const std.Target) u8 { | |
| 1124 | const target_version = target.os.versionRange().gnuLibCVersion() orelse return 0; | |
| 1125 | var count: u8 = 0; | |
| 1126 | for (libs) |lib| { | |
| 1127 | if (lib.removed_in) |rem_in| { | |
| 1128 | if (target_version.order(rem_in) != .lt) continue; | |
| 1129 | } | |
| 1130 | count += 1; | |
| 1131 | } | |
| 1132 | return count; | |
| 1133 | } | |
| 1134 | ||
| 1135 | 1123 | fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { |
| 1136 | 1124 | const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?; |
| 1137 | 1125 |
src/libs/mingw.zig+2-1| ... | ... | @@ -1011,7 +1011,6 @@ const mingw32_winpthreads_src = [_][]const u8{ |
| 1011 | 1011 | "winpthreads" ++ path.sep_str ++ "thread.c", |
| 1012 | 1012 | }; |
| 1013 | 1013 | |
| 1014 | // Note: kernel32 and ntdll are always linked even without targeting MinGW-w64. | |
| 1015 | 1014 | pub const always_link_libs = [_][]const u8{ |
| 1016 | 1015 | "api-ms-win-crt-conio-l1-1-0", |
| 1017 | 1016 | "api-ms-win-crt-convert-l1-1-0", |
| ... | ... | @@ -1029,6 +1028,8 @@ pub const always_link_libs = [_][]const u8{ |
| 1029 | 1028 | "api-ms-win-crt-time-l1-1-0", |
| 1030 | 1029 | "api-ms-win-crt-utility-l1-1-0", |
| 1031 | 1030 | "advapi32", |
| 1031 | "kernel32", | |
| 1032 | "ntdll", | |
| 1032 | 1033 | "shell32", |
| 1033 | 1034 | "user32", |
| 1034 | 1035 | }; |
src/libs/netbsd.zig-4| ... | ... | @@ -642,10 +642,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye |
| 642 | 642 | }); |
| 643 | 643 | } |
| 644 | 644 | |
| 645 | pub fn sharedObjectsCount() u8 { | |
| 646 | return libs.len; | |
| 647 | } | |
| 648 | ||
| 649 | 645 | fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void { |
| 650 | 646 | assert(comp.netbsd_so_files == null); |
| 651 | 647 | comp.netbsd_so_files = so_files; |
src/link/MachO/dyld_info/Trie.zig+16-11| ... | ... | @@ -138,18 +138,23 @@ fn finalize(self: *Trie, allocator: Allocator) !void { |
| 138 | 138 | defer ordered_nodes.deinit(); |
| 139 | 139 | try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len); |
| 140 | 140 | |
| 141 | var fifo = DeprecatedLinearFifo(Node.Index).init(allocator); | |
| 142 | defer fifo.deinit(); | |
| 143 | ||
| 144 | try fifo.writeItem(self.root.?); | |
| 145 | ||
| 146 | while (fifo.readItem()) |next_index| { | |
| 147 | const edges = &self.nodes.items(.edges)[next_index]; | |
| 148 | for (edges.items) |edge_index| { | |
| 149 | const edge = self.edges.items[edge_index]; | |
| 150 | try fifo.writeItem(edge.node); | |
| 141 | { | |
| 142 | var fifo: std.ArrayListUnmanaged(Node.Index) = .empty; | |
| 143 | defer fifo.deinit(allocator); | |
| 144 | ||
| 145 | try fifo.append(allocator, self.root.?); | |
| 146 | ||
| 147 | var i: usize = 0; | |
| 148 | while (i < fifo.items.len) { | |
| 149 | const next_index = fifo.items[i]; | |
| 150 | i += 1; | |
| 151 | const edges = &self.nodes.items(.edges)[next_index]; | |
| 152 | for (edges.items) |edge_index| { | |
| 153 | const edge = self.edges.items[edge_index]; | |
| 154 | try fifo.append(allocator, edge.node); | |
| 155 | } | |
| 156 | ordered_nodes.appendAssumeCapacity(next_index); | |
| 151 | 157 | } |
| 152 | ordered_nodes.appendAssumeCapacity(next_index); | |
| 153 | 158 | } |
| 154 | 159 | |
| 155 | 160 | var more: bool = true; |
src/link/Queue.zig+45-13| ... | ... | @@ -16,9 +16,9 @@ mutex: std.Thread.Mutex, |
| 16 | 16 | /// Validates that only one `flushTaskQueue` thread is running at a time. |
| 17 | 17 | flush_safety: std.debug.SafetyLock, |
| 18 | 18 | |
| 19 | /// This is the number of prelink tasks which are expected but have not yet been enqueued. | |
| 20 | /// Guarded by `mutex`. | |
| 21 | pending_prelink_tasks: u32, | |
| 19 | /// This value is positive while there are still prelink tasks yet to be queued. Once they are | |
| 20 | /// all queued, this value becomes 0, and ZCU tasks can be run. Guarded by `mutex`. | |
| 21 | prelink_wait_count: u32, | |
| 22 | 22 | |
| 23 | 23 | /// Prelink tasks which have been enqueued and are not yet owned by the worker thread. |
| 24 | 24 | /// Allocated into `gpa`, guarded by `mutex`. |
| ... | ... | @@ -59,7 +59,7 @@ state: union(enum) { |
| 59 | 59 | /// The link thread is currently running or queued to run. |
| 60 | 60 | running, |
| 61 | 61 | /// The link thread is not running or queued, because it has exhausted all immediately available |
| 62 | /// tasks. It should be spawned when more tasks are enqueued. If `pending_prelink_tasks` is not | |
| 62 | /// tasks. It should be spawned when more tasks are enqueued. If `prelink_wait_count` is not | |
| 63 | 63 | /// zero, we are specifically waiting for prelink tasks. |
| 64 | 64 | finished, |
| 65 | 65 | /// The link thread is not running or queued, because it is waiting for this MIR to be populated. |
| ... | ... | @@ -73,11 +73,11 @@ state: union(enum) { |
| 73 | 73 | const max_air_bytes_in_flight = 10 * 1024 * 1024; |
| 74 | 74 | |
| 75 | 75 | /// The initial `Queue` state, containing no tasks, expecting no prelink tasks, and with no running worker thread. |
| 76 | /// The `pending_prelink_tasks` and `queued_prelink` fields may be modified as needed before calling `start`. | |
| 76 | /// The `queued_prelink` field may be appended to before calling `start`. | |
| 77 | 77 | pub const empty: Queue = .{ |
| 78 | 78 | .mutex = .{}, |
| 79 | 79 | .flush_safety = .{}, |
| 80 | .pending_prelink_tasks = 0, | |
| 80 | .prelink_wait_count = undefined, // set in `start` | |
| 81 | 81 | .queued_prelink = .empty, |
| 82 | 82 | .wip_prelink = .empty, |
| 83 | 83 | .queued_zcu = .empty, |
| ... | ... | @@ -100,17 +100,49 @@ pub fn deinit(q: *Queue, comp: *Compilation) void { |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | 102 | /// This is expected to be called exactly once, after which the caller must not directly access |
| 103 | /// `queued_prelink` or `pending_prelink_tasks` any longer. This will spawn the link thread if | |
| 104 | /// necessary. | |
| 103 | /// `queued_prelink` any longer. This will spawn the link thread if necessary. | |
| 105 | 104 | pub fn start(q: *Queue, comp: *Compilation) void { |
| 106 | 105 | assert(q.state == .finished); |
| 107 | 106 | assert(q.queued_zcu.items.len == 0); |
| 107 | // Reset this to 1. We can't init it to 1 in `empty`, because it would fall to 0 on successive | |
| 108 | // incremental updates, but we still need the initial 1. | |
| 109 | q.prelink_wait_count = 1; | |
| 108 | 110 | if (q.queued_prelink.items.len != 0) { |
| 109 | 111 | q.state = .running; |
| 110 | 112 | comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp }); |
| 111 | 113 | } |
| 112 | 114 | } |
| 113 | 115 | |
| 116 | /// Every call to this must be paired with a call to `finishPrelinkItem`. | |
| 117 | pub fn startPrelinkItem(q: *Queue) void { | |
| 118 | q.mutex.lock(); | |
| 119 | defer q.mutex.unlock(); | |
| 120 | assert(q.prelink_wait_count > 0); // must not have finished everything already | |
| 121 | q.prelink_wait_count += 1; | |
| 122 | } | |
| 123 | /// This function must be called exactly one more time than `startPrelinkItem` is. The final call | |
| 124 | /// indicates that we have finished calling `startPrelinkItem`, so once all pending items finish, | |
| 125 | /// we are ready to move on to ZCU tasks. | |
| 126 | pub fn finishPrelinkItem(q: *Queue, comp: *Compilation) void { | |
| 127 | { | |
| 128 | q.mutex.lock(); | |
| 129 | defer q.mutex.unlock(); | |
| 130 | q.prelink_wait_count -= 1; | |
| 131 | if (q.prelink_wait_count != 0) return; | |
| 132 | // The prelink task count dropped to 0; restart the linker thread if necessary. | |
| 133 | switch (q.state) { | |
| 134 | .wait_for_mir => unreachable, // we've not started zcu tasks yet | |
| 135 | .running => return, | |
| 136 | .finished => {}, | |
| 137 | } | |
| 138 | assert(q.queued_prelink.items.len == 0); | |
| 139 | // Even if there are no ZCU tasks, we must restart the linker thread to make sure | |
| 140 | // that `link.File.prelink()` is called. | |
| 141 | q.state = .running; | |
| 142 | } | |
| 143 | comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp }); | |
| 144 | } | |
| 145 | ||
| 114 | 146 | /// Called by codegen workers after they have populated a `ZcuTask.LinkFunc.SharedMir`. If the link |
| 115 | 147 | /// thread was waiting for this MIR, it can resume. |
| 116 | 148 | pub fn mirReady(q: *Queue, comp: *Compilation, func_index: InternPool.Index, mir: *ZcuTask.LinkFunc.SharedMir) void { |
| ... | ... | @@ -130,14 +162,14 @@ pub fn mirReady(q: *Queue, comp: *Compilation, func_index: InternPool.Index, mir |
| 130 | 162 | comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp }); |
| 131 | 163 | } |
| 132 | 164 | |
| 133 | /// Enqueues all prelink tasks in `tasks`. Asserts that they were expected, i.e. that `tasks.len` is | |
| 134 | /// less than or equal to `q.pending_prelink_tasks`. Also asserts that `tasks.len` is not 0. | |
| 165 | /// Enqueues all prelink tasks in `tasks`. Asserts that they were expected, i.e. that | |
| 166 | /// `prelink_wait_count` is not yet 0. Also asserts that `tasks.len` is not 0. | |
| 135 | 167 | pub fn enqueuePrelink(q: *Queue, comp: *Compilation, tasks: []const PrelinkTask) Allocator.Error!void { |
| 136 | 168 | { |
| 137 | 169 | q.mutex.lock(); |
| 138 | 170 | defer q.mutex.unlock(); |
| 171 | assert(q.prelink_wait_count > 0); | |
| 139 | 172 | try q.queued_prelink.appendSlice(comp.gpa, tasks); |
| 140 | q.pending_prelink_tasks -= @intCast(tasks.len); | |
| 141 | 173 | switch (q.state) { |
| 142 | 174 | .wait_for_mir => unreachable, // we've not started zcu tasks yet |
| 143 | 175 | .running => return, |
| ... | ... | @@ -167,7 +199,7 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error! |
| 167 | 199 | try q.queued_zcu.append(comp.gpa, task); |
| 168 | 200 | switch (q.state) { |
| 169 | 201 | .running, .wait_for_mir => return, |
| 170 | .finished => if (q.pending_prelink_tasks != 0) return, | |
| 202 | .finished => if (q.prelink_wait_count > 0) return, | |
| 171 | 203 | } |
| 172 | 204 | // Restart the linker thread, unless it would immediately be blocked |
| 173 | 205 | if (task == .link_func and task.link_func.mir.status.load(.acquire) == .pending) { |
| ... | ... | @@ -194,7 +226,7 @@ fn flushTaskQueue(tid: usize, q: *Queue, comp: *Compilation) void { |
| 194 | 226 | defer q.mutex.unlock(); |
| 195 | 227 | std.mem.swap(std.ArrayListUnmanaged(PrelinkTask), &q.queued_prelink, &q.wip_prelink); |
| 196 | 228 | if (q.wip_prelink.items.len == 0) { |
| 197 | if (q.pending_prelink_tasks == 0) { | |
| 229 | if (q.prelink_wait_count == 0) { | |
| 198 | 230 | break :prelink; // prelink is done |
| 199 | 231 | } else { |
| 200 | 232 | // We're expecting more prelink tasks so can't move on to ZCU tasks. |
src/main.zig+23-23| ... | ... | @@ -312,7 +312,6 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 312 | 312 | return jitCmd(gpa, arena, cmd_args, .{ |
| 313 | 313 | .cmd_name = "resinator", |
| 314 | 314 | .root_src_path = "resinator/main.zig", |
| 315 | .windows_libs = &.{"advapi32"}, | |
| 316 | 315 | .depend_on_aro = true, |
| 317 | 316 | .prepend_zig_lib_dir_path = true, |
| 318 | 317 | .server = use_server, |
| ... | ... | @@ -337,7 +336,6 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 337 | 336 | return jitCmd(gpa, arena, cmd_args, .{ |
| 338 | 337 | .cmd_name = "std", |
| 339 | 338 | .root_src_path = "std-docs.zig", |
| 340 | .windows_libs = &.{"ws2_32"}, | |
| 341 | 339 | .prepend_zig_lib_dir_path = true, |
| 342 | 340 | .prepend_zig_exe_path = true, |
| 343 | 341 | .prepend_global_cache_path = true, |
| ... | ... | @@ -3659,6 +3657,7 @@ fn buildOutputType( |
| 3659 | 3657 | } else if (target.os.tag == .windows) { |
| 3660 | 3658 | try test_exec_args.appendSlice(arena, &.{ |
| 3661 | 3659 | "--subsystem", "console", |
| 3660 | "-lkernel32", "-lntdll", | |
| 3662 | 3661 | }); |
| 3663 | 3662 | } |
| 3664 | 3663 | |
| ... | ... | @@ -3862,8 +3861,7 @@ fn createModule( |
| 3862 | 3861 | .only_compiler_rt => continue, |
| 3863 | 3862 | } |
| 3864 | 3863 | |
| 3865 | // We currently prefer import libraries provided by MinGW-w64 even for MSVC. | |
| 3866 | if (target.os.tag == .windows) { | |
| 3864 | if (target.isMinGW()) { | |
| 3867 | 3865 | const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| { |
| 3868 | 3866 | fatal("failed to check zig installation for DLL import libs: {s}", .{ |
| 3869 | 3867 | @errorName(err), |
| ... | ... | @@ -4796,7 +4794,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4796 | 4794 | writeSimpleTemplateFile(Package.Manifest.basename, |
| 4797 | 4795 | \\.{{ |
| 4798 | 4796 | \\ .name = .{s}, |
| 4799 | \\ .version = "{s}", | |
| 4797 | \\ .version = "0.0.1", | |
| 4798 | \\ .minimum_zig_version = "{s}", | |
| 4800 | 4799 | \\ .paths = .{{""}}, |
| 4801 | 4800 | \\ .fingerprint = 0x{x}, |
| 4802 | 4801 | \\}} |
| ... | ... | @@ -4811,6 +4810,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4811 | 4810 | }; |
| 4812 | 4811 | writeSimpleTemplateFile(Package.build_zig_basename, |
| 4813 | 4812 | \\const std = @import("std"); |
| 4813 | \\ | |
| 4814 | 4814 | \\pub fn build(b: *std.Build) void {{ |
| 4815 | 4815 | \\ _ = b; // stub |
| 4816 | 4816 | \\}} |
| ... | ... | @@ -4891,6 +4891,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 4891 | 4891 | var fetch_mode: Package.Fetch.JobQueue.Mode = .needed; |
| 4892 | 4892 | var system_pkg_dir_path: ?[]const u8 = null; |
| 4893 | 4893 | var debug_target: ?[]const u8 = null; |
| 4894 | var debug_libc_paths_file: ?[]const u8 = null; | |
| 4894 | 4895 | |
| 4895 | 4896 | const argv_index_exe = child_argv.items.len; |
| 4896 | 4897 | _ = try child_argv.addOne(); |
| ... | ... | @@ -5014,6 +5015,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5014 | 5015 | } else { |
| 5015 | 5016 | warn("Zig was compiled without debug extensions. --debug-target has no effect.", .{}); |
| 5016 | 5017 | } |
| 5018 | } else if (mem.eql(u8, arg, "--debug-libc")) { | |
| 5019 | if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); | |
| 5020 | i += 1; | |
| 5021 | if (build_options.enable_debug_extensions) { | |
| 5022 | debug_libc_paths_file = args[i]; | |
| 5023 | } else { | |
| 5024 | warn("Zig was compiled without debug extensions. --debug-libc has no effect.", .{}); | |
| 5025 | } | |
| 5017 | 5026 | } else if (mem.eql(u8, arg, "--verbose-link")) { |
| 5018 | 5027 | verbose_link = true; |
| 5019 | 5028 | } else if (mem.eql(u8, arg, "--verbose-cc")) { |
| ... | ... | @@ -5101,6 +5110,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5101 | 5110 | .is_explicit_dynamic_linker = false, |
| 5102 | 5111 | }; |
| 5103 | 5112 | }; |
| 5113 | // Likewise, `--debug-libc` allows overriding the libc installation. | |
| 5114 | const libc_installation: ?*const LibCInstallation = lci: { | |
| 5115 | const paths_file = debug_libc_paths_file orelse break :lci null; | |
| 5116 | if (!build_options.enable_debug_extensions) unreachable; | |
| 5117 | const lci = try arena.create(LibCInstallation); | |
| 5118 | lci.* = try .parse(arena, paths_file, &resolved_target.result); | |
| 5119 | break :lci lci; | |
| 5120 | }; | |
| 5104 | 5121 | |
| 5105 | 5122 | process.raiseFileDescriptorLimit(); |
| 5106 | 5123 | |
| ... | ... | @@ -5356,15 +5373,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5356 | 5373 | |
| 5357 | 5374 | try root_mod.deps.put(arena, "@build", build_mod); |
| 5358 | 5375 | |
| 5359 | var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; | |
| 5360 | ||
| 5361 | if (resolved_target.result.os.tag == .windows) { | |
| 5362 | try windows_libs.ensureUnusedCapacity(arena, 2); | |
| 5363 | windows_libs.putAssumeCapacity("advapi32", {}); | |
| 5364 | windows_libs.putAssumeCapacity("ws2_32", {}); // for `--listen` (web interface) | |
| 5365 | } | |
| 5366 | ||
| 5367 | 5376 | const comp = Compilation.create(gpa, arena, .{ |
| 5377 | .libc_installation = libc_installation, | |
| 5368 | 5378 | .dirs = dirs, |
| 5369 | 5379 | .root_name = "build", |
| 5370 | 5380 | .config = config, |
| ... | ... | @@ -5385,7 +5395,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5385 | 5395 | .cache_mode = .whole, |
| 5386 | 5396 | .reference_trace = reference_trace, |
| 5387 | 5397 | .debug_compile_errors = debug_compile_errors, |
| 5388 | .windows_lib_names = windows_libs.keys(), | |
| 5389 | 5398 | }) catch |err| { |
| 5390 | 5399 | fatal("unable to create compilation: {s}", .{@errorName(err)}); |
| 5391 | 5400 | }; |
| ... | ... | @@ -5489,7 +5498,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 5489 | 5498 | const JitCmdOptions = struct { |
| 5490 | 5499 | cmd_name: []const u8, |
| 5491 | 5500 | root_src_path: []const u8, |
| 5492 | windows_libs: []const []const u8 = &.{}, | |
| 5493 | 5501 | prepend_zig_lib_dir_path: bool = false, |
| 5494 | 5502 | prepend_global_cache_path: bool = false, |
| 5495 | 5503 | prepend_zig_exe_path: bool = false, |
| ... | ... | @@ -5606,13 +5614,6 @@ fn jitCmd( |
| 5606 | 5614 | try root_mod.deps.put(arena, "aro", aro_mod); |
| 5607 | 5615 | } |
| 5608 | 5616 | |
| 5609 | var windows_libs: std.StringArrayHashMapUnmanaged(void) = .empty; | |
| 5610 | ||
| 5611 | if (resolved_target.result.os.tag == .windows) { | |
| 5612 | try windows_libs.ensureUnusedCapacity(arena, options.windows_libs.len); | |
| 5613 | for (options.windows_libs) |lib| windows_libs.putAssumeCapacity(lib, {}); | |
| 5614 | } | |
| 5615 | ||
| 5616 | 5617 | const comp = Compilation.create(gpa, arena, .{ |
| 5617 | 5618 | .dirs = dirs, |
| 5618 | 5619 | .root_name = options.cmd_name, |
| ... | ... | @@ -5623,7 +5624,6 @@ fn jitCmd( |
| 5623 | 5624 | .self_exe_path = self_exe_path, |
| 5624 | 5625 | .thread_pool = &thread_pool, |
| 5625 | 5626 | .cache_mode = .whole, |
| 5626 | .windows_lib_names = windows_libs.keys(), | |
| 5627 | 5627 | }) catch |err| { |
| 5628 | 5628 | fatal("unable to create compilation: {s}", .{@errorName(err)}); |
| 5629 | 5629 | }; |
src/target.zig+1-1| ... | ... | @@ -20,7 +20,7 @@ pub fn cannotDynamicLink(target: *const std.Target) bool { |
| 20 | 20 | /// Similarly on FreeBSD and NetBSD we always link system libc |
| 21 | 21 | /// since this is the stable syscall interface. |
| 22 | 22 | pub fn osRequiresLibC(target: *const std.Target) bool { |
| 23 | return target.os.requiresLibC(); | |
| 23 | return target.requiresLibC(); | |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | pub fn libCNeedsLibUnwind(target: *const std.Target, link_mode: std.builtin.LinkMode) bool { |
test/behavior/tuple.zig+4-2| ... | ... | @@ -318,6 +318,8 @@ test "tuple type with void field" { |
| 318 | 318 | test "zero sized struct in tuple handled correctly" { |
| 319 | 319 | const State = struct { |
| 320 | 320 | const Self = @This(); |
| 321 | const Inner = struct {}; | |
| 322 | ||
| 321 | 323 | data: @Type(.{ |
| 322 | 324 | .@"struct" = .{ |
| 323 | 325 | .is_tuple = true, |
| ... | ... | @@ -325,10 +327,10 @@ test "zero sized struct in tuple handled correctly" { |
| 325 | 327 | .decls = &.{}, |
| 326 | 328 | .fields = &.{.{ |
| 327 | 329 | .name = "0", |
| 328 | .type = struct {}, | |
| 330 | .type = Inner, | |
| 329 | 331 | .default_value_ptr = null, |
| 330 | 332 | .is_comptime = false, |
| 331 | .alignment = 0, | |
| 333 | .alignment = @alignOf(Inner), | |
| 332 | 334 | }}, |
| 333 | 335 | }, |
| 334 | 336 | }), |
test/behavior/type.zig+3-3| ... | ... | @@ -433,8 +433,8 @@ test "Type.Union" { |
| 433 | 433 | .layout = .@"packed", |
| 434 | 434 | .tag_type = null, |
| 435 | 435 | .fields = &.{ |
| 436 | .{ .name = "signed", .type = i32, .alignment = @alignOf(i32) }, | |
| 437 | .{ .name = "unsigned", .type = u32, .alignment = @alignOf(u32) }, | |
| 436 | .{ .name = "signed", .type = i32, .alignment = 0 }, | |
| 437 | .{ .name = "unsigned", .type = u32, .alignment = 0 }, | |
| 438 | 438 | }, |
| 439 | 439 | .decls = &.{}, |
| 440 | 440 | }, |
| ... | ... | @@ -735,7 +735,7 @@ test "struct field names sliced at comptime from larger string" { |
| 735 | 735 | var it = std.mem.tokenizeScalar(u8, text, '\n'); |
| 736 | 736 | while (it.next()) |name| { |
| 737 | 737 | fields = fields ++ &[_]Type.StructField{.{ |
| 738 | .alignment = 0, | |
| 738 | .alignment = @alignOf(usize), | |
| 739 | 739 | .name = name ++ "", |
| 740 | 740 | .type = usize, |
| 741 | 741 | .default_value_ptr = null, |
test/behavior/union.zig+8| ... | ... | @@ -2311,3 +2311,11 @@ test "set mutable union by switching on same union" { |
| 2311 | 2311 | try expect(val == .bar); |
| 2312 | 2312 | try expect(val.bar == 2); |
| 2313 | 2313 | } |
| 2314 | ||
| 2315 | test "initialize empty field of union inside comptime-known struct constant" { | |
| 2316 | const Inner = union { none: void, some: u8 }; | |
| 2317 | const Wrapper = struct { inner: Inner }; | |
| 2318 | ||
| 2319 | const val: Wrapper = .{ .inner = .{ .none = {} } }; | |
| 2320 | comptime assert(val.inner.none == {}); | |
| 2321 | } |
test/cases/compile_errors/align_zero.zig+44-16| ... | ... | @@ -1,52 +1,80 @@ |
| 1 | pub var global_var: i32 align(0) = undefined; | |
| 1 | var global_var: i32 align(0) = undefined; | |
| 2 | 2 | |
| 3 | pub export fn a() void { | |
| 3 | export fn a() void { | |
| 4 | 4 | _ = &global_var; |
| 5 | 5 | } |
| 6 | 6 | |
| 7 | pub extern var extern_var: i32 align(0); | |
| 7 | extern var extern_var: i32 align(0); | |
| 8 | 8 | |
| 9 | pub export fn b() void { | |
| 9 | export fn b() void { | |
| 10 | 10 | _ = &extern_var; |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | pub export fn c() align(0) void {} | |
| 13 | export fn c() align(0) void {} | |
| 14 | 14 | |
| 15 | pub export fn d() void { | |
| 15 | export fn d() void { | |
| 16 | 16 | _ = *align(0) fn () i32; |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | pub export fn e() void { | |
| 19 | export fn e() void { | |
| 20 | 20 | var local_var: i32 align(0) = undefined; |
| 21 | 21 | _ = &local_var; |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | pub export fn f() void { | |
| 24 | export fn f() void { | |
| 25 | 25 | _ = *align(0) i32; |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | pub export fn g() void { | |
| 28 | export fn g() void { | |
| 29 | 29 | _ = []align(0) i32; |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | pub export fn h() void { | |
| 32 | export fn h() void { | |
| 33 | 33 | _ = struct { field: i32 align(0) }; |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | pub export fn i() void { | |
| 36 | export fn i() void { | |
| 37 | 37 | _ = union { field: i32 align(0) }; |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | export fn j() void { | |
| 41 | _ = @Type(.{ .@"struct" = .{ | |
| 42 | .layout = .auto, | |
| 43 | .fields = &.{.{ | |
| 44 | .name = "test", | |
| 45 | .type = u32, | |
| 46 | .default_value_ptr = null, | |
| 47 | .is_comptime = false, | |
| 48 | .alignment = 0, | |
| 49 | }}, | |
| 50 | .decls = &.{}, | |
| 51 | .is_tuple = false, | |
| 52 | } }); | |
| 53 | } | |
| 54 | ||
| 55 | export fn k() void { | |
| 56 | _ = @Type(.{ .pointer = .{ | |
| 57 | .size = .one, | |
| 58 | .is_const = false, | |
| 59 | .is_volatile = false, | |
| 60 | .alignment = 0, | |
| 61 | .address_space = .generic, | |
| 62 | .child = u32, | |
| 63 | .is_allowzero = false, | |
| 64 | .sentinel_ptr = null, | |
| 65 | } }); | |
| 66 | } | |
| 67 | ||
| 40 | 68 | // error |
| 41 | // backend=stage2 | |
| 42 | // target=native | |
| 43 | 69 | // |
| 44 | // :1:31: error: alignment must be >= 1 | |
| 45 | // :7:38: error: alignment must be >= 1 | |
| 46 | // :13:25: error: alignment must be >= 1 | |
| 70 | // :1:27: error: alignment must be >= 1 | |
| 71 | // :7:34: error: alignment must be >= 1 | |
| 72 | // :13:21: error: alignment must be >= 1 | |
| 47 | 73 | // :16:16: error: alignment must be >= 1 |
| 48 | 74 | // :20:30: error: alignment must be >= 1 |
| 49 | 75 | // :25:16: error: alignment must be >= 1 |
| 50 | 76 | // :29:17: error: alignment must be >= 1 |
| 51 | 77 | // :33:35: error: alignment must be >= 1 |
| 52 | 78 | // :37:34: error: alignment must be >= 1 |
| 79 | // :41:9: error: alignment must be >= 1 | |
| 80 | // :56:9: error: alignment must be >= 1 |
test/cases/compile_errors/bad_alignment_type.zig+2-2| ... | ... | @@ -11,5 +11,5 @@ export fn entry2() void { |
| 11 | 11 | // backend=stage2 |
| 12 | 12 | // target=native |
| 13 | 13 | // |
| 14 | // :2:22: error: expected type 'u32', found 'bool' | |
| 15 | // :6:21: error: fractional component prevents float value '12.34' from coercion to type 'u32' | |
| 14 | // :2:22: error: expected type 'u29', found 'bool' | |
| 15 | // :6:21: error: fractional component prevents float value '12.34' from coercion to type 'u29' |
test/cases/compile_errors/packed_struct_field_alignment_unavailable_for_reify_type.zig deleted-9| ... | ... | @@ -1,9 +0,0 @@ |
| 1 | export fn entry() void { | |
| 2 | _ = @Type(.{ .@"struct" = .{ .layout = .@"packed", .fields = &.{ | |
| 3 | .{ .name = "one", .type = u4, .default_value_ptr = null, .is_comptime = false, .alignment = 2 }, | |
| 4 | }, .decls = &.{}, .is_tuple = false } }); | |
| 5 | } | |
| 6 | ||
| 7 | // error | |
| 8 | // | |
| 9 | // :2:9: error: alignment in a packed struct field must be set to 0 |
test/cases/compile_errors/packed_union_alignment_override.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const U = packed union { | |
| 2 | x: f32, | |
| 3 | y: u8 align(10), | |
| 4 | z: u32, | |
| 5 | }; | |
| 6 | ||
| 7 | // error | |
| 8 | // | |
| 9 | // :3:17: error: unable to override alignment of packed union fields |
test/cases/compile_errors/reify_struct.zig+2-1| ... | ... | @@ -75,4 +75,5 @@ comptime { |
| 75 | 75 | // :16:5: error: tuple field name '3' does not match field index 0 |
| 76 | 76 | // :30:5: error: comptime field without default initialization value |
| 77 | 77 | // :44:5: error: extern struct fields cannot be marked comptime |
| 78 | // :58:5: error: alignment in a packed struct field must be set to 0 | |
| 78 | // :58:5: error: alignment of a packed struct field must be set to 0 | |
| 79 |
test/cases/compile_errors/reify_type_with_invalid_field_alignment.zig+3-3| ... | ... | @@ -43,6 +43,6 @@ comptime { |
| 43 | 43 | |
| 44 | 44 | // error |
| 45 | 45 | // |
| 46 | // :2:9: error: alignment value '3' is not a power of two or zero | |
| 47 | // :14:9: error: alignment value '5' is not a power of two or zero | |
| 48 | // :30:9: error: alignment value '7' is not a power of two or zero | |
| 46 | // :2:9: error: alignment value '3' is not a power of two | |
| 47 | // :14:9: error: alignment value '5' is not a power of two | |
| 48 | // :30:9: error: alignment value '7' is not a power of two |
test/src/Cases.zig+3| ... | ... | @@ -593,6 +593,7 @@ pub fn lowerToTranslateCSteps( |
| 593 | 593 | pub const CaseTestOptions = struct { |
| 594 | 594 | test_filters: []const []const u8, |
| 595 | 595 | test_target_filters: []const []const u8, |
| 596 | skip_compile_errors: bool, | |
| 596 | 597 | skip_non_native: bool, |
| 597 | 598 | skip_freebsd: bool, |
| 598 | 599 | skip_netbsd: bool, |
| ... | ... | @@ -618,6 +619,8 @@ pub fn lowerToBuildSteps( |
| 618 | 619 | if (std.mem.indexOf(u8, case.name, test_filter)) |_| break; |
| 619 | 620 | } else if (options.test_filters.len > 0) continue; |
| 620 | 621 | |
| 622 | if (case.case.? == .Error and options.skip_compile_errors) continue; | |
| 623 | ||
| 621 | 624 | if (options.skip_non_native and !case.target.query.isNative()) |
| 622 | 625 | continue; |
| 623 | 626 |
test/standalone/child_process/build.zig+11| ... | ... | @@ -31,5 +31,16 @@ pub fn build(b: *std.Build) void { |
| 31 | 31 | run.addArtifactArg(child); |
| 32 | 32 | run.expectExitCode(0); |
| 33 | 33 | |
| 34 | // Use a temporary directory within the cache as the CWD to test | |
| 35 | // spawning the child using a path that contains a leading `..` component. | |
| 36 | const run_relative = b.addRunArtifact(main); | |
| 37 | run_relative.addArtifactArg(child); | |
| 38 | const write_tmp_dir = b.addWriteFiles(); | |
| 39 | const tmp_cwd = write_tmp_dir.getDirectory(); | |
| 40 | run_relative.addDirectoryArg(tmp_cwd); | |
| 41 | run_relative.setCwd(tmp_cwd); | |
| 42 | run_relative.expectExitCode(0); | |
| 43 | ||
| 34 | 44 | test_step.dependOn(&run.step); |
| 45 | test_step.dependOn(&run_relative.step); | |
| 35 | 46 | } |
test/standalone/child_process/main.zig+14-2| ... | ... | @@ -11,7 +11,14 @@ pub fn main() !void { |
| 11 | 11 | var it = try std.process.argsWithAllocator(gpa); |
| 12 | 12 | defer it.deinit(); |
| 13 | 13 | _ = it.next() orelse unreachable; // skip binary name |
| 14 | const child_path = it.next() orelse unreachable; | |
| 14 | const child_path, const needs_free = child_path: { | |
| 15 | const child_path = it.next() orelse unreachable; | |
| 16 | const cwd_path = it.next() orelse break :child_path .{ child_path, false }; | |
| 17 | // If there is a third argument, it is the current CWD somewhere within the cache directory. | |
| 18 | // In that case, modify the child path in order to test spawning a path with a leading `..` component. | |
| 19 | break :child_path .{ try std.fs.path.relative(gpa, cwd_path, child_path), true }; | |
| 20 | }; | |
| 21 | defer if (needs_free) gpa.free(child_path); | |
| 15 | 22 | |
| 16 | 23 | var child = std.process.Child.init(&.{ child_path, "hello arg" }, gpa); |
| 17 | 24 | child.stdin_behavior = .Pipe; |
| ... | ... | @@ -39,7 +46,12 @@ pub fn main() !void { |
| 39 | 46 | }, |
| 40 | 47 | else => |term| testError("abnormal child exit: {}", .{term}), |
| 41 | 48 | } |
| 42 | return if (parent_test_error) error.ParentTestError else {}; | |
| 49 | if (parent_test_error) return error.ParentTestError; | |
| 50 | ||
| 51 | // Check that FileNotFound is consistent across platforms when trying to spawn an executable that doesn't exist | |
| 52 | const missing_child_path = try std.mem.concat(gpa, u8, &.{ child_path, "_intentionally_missing" }); | |
| 53 | defer gpa.free(missing_child_path); | |
| 54 | try std.testing.expectError(error.FileNotFound, std.process.Child.run(.{ .allocator = gpa, .argv = &.{missing_child_path} })); | |
| 43 | 55 | } |
| 44 | 56 | |
| 45 | 57 | var parent_test_error = false; |
test/standalone/simple/build.zig-8| ... | ... | @@ -50,10 +50,6 @@ pub fn build(b: *std.Build) void { |
| 50 | 50 | }); |
| 51 | 51 | if (case.link_libc) exe.root_module.link_libc = true; |
| 52 | 52 | |
| 53 | if (resolved_target.result.os.tag == .windows) { | |
| 54 | exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 55 | } | |
| 56 | ||
| 57 | 53 | _ = exe.getEmittedBin(); |
| 58 | 54 | |
| 59 | 55 | step.dependOn(&exe.step); |
| ... | ... | @@ -70,10 +66,6 @@ pub fn build(b: *std.Build) void { |
| 70 | 66 | }); |
| 71 | 67 | if (case.link_libc) exe.root_module.link_libc = true; |
| 72 | 68 | |
| 73 | if (resolved_target.result.os.tag == .windows) { | |
| 74 | exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 75 | } | |
| 76 | ||
| 77 | 69 | const run = b.addRunArtifact(exe); |
| 78 | 70 | step.dependOn(&run.step); |
| 79 | 71 | } |
test/standalone/windows_argv/build.zig-2| ... | ... | @@ -47,8 +47,6 @@ pub fn build(b: *std.Build) !void { |
| 47 | 47 | }), |
| 48 | 48 | }); |
| 49 | 49 | |
| 50 | fuzz.root_module.linkSystemLibrary("advapi32", .{}); | |
| 51 | ||
| 52 | 50 | const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; |
| 53 | 51 | const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); |
| 54 | 52 |
test/standalone/windows_bat_args/build.zig-4| ... | ... | @@ -28,8 +28,6 @@ pub fn build(b: *std.Build) !void { |
| 28 | 28 | }), |
| 29 | 29 | }); |
| 30 | 30 | |
| 31 | test_exe.root_module.linkSystemLibrary("advapi32", .{}); | |
| 32 | ||
| 33 | 31 | const run = b.addRunArtifact(test_exe); |
| 34 | 32 | run.addArtifactArg(echo_args); |
| 35 | 33 | run.expectExitCode(0); |
| ... | ... | @@ -46,8 +44,6 @@ pub fn build(b: *std.Build) !void { |
| 46 | 44 | }), |
| 47 | 45 | }); |
| 48 | 46 | |
| 49 | fuzz.root_module.linkSystemLibrary("advapi32", .{}); | |
| 50 | ||
| 51 | 47 | const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100; |
| 52 | 48 | const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom"); |
| 53 | 49 |
test/standalone/windows_spawn/build.zig-2| ... | ... | @@ -28,8 +28,6 @@ pub fn build(b: *std.Build) void { |
| 28 | 28 | }), |
| 29 | 29 | }); |
| 30 | 30 | |
| 31 | main.root_module.linkSystemLibrary("advapi32", .{}); | |
| 32 | ||
| 33 | 31 | const run = b.addRunArtifact(main); |
| 34 | 32 | run.addArtifactArg(hello); |
| 35 | 33 | run.expectExitCode(0); |
test/standalone/windows_spawn/main.zig+46| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | ||
| 2 | 3 | const windows = std.os.windows; |
| 3 | 4 | const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral; |
| 4 | 5 | |
| ... | ... | @@ -39,6 +40,9 @@ pub fn main() anyerror!void { |
| 39 | 40 | // No PATH, so it should fail to find anything not in the cwd |
| 40 | 41 | try testExecError(error.FileNotFound, allocator, "something_missing"); |
| 41 | 42 | |
| 43 | // make sure we don't get error.BadPath traversing out of cwd with a relative path | |
| 44 | try testExecError(error.FileNotFound, allocator, "..\\.\\.\\.\\\\..\\more_missing"); | |
| 45 | ||
| 42 | 46 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( |
| 43 | 47 | utf16Literal("PATH"), |
| 44 | 48 | tmp_absolute_path_w, |
| ... | ... | @@ -149,6 +153,48 @@ pub fn main() anyerror!void { |
| 149 | 153 | // If we try to exec but provide a cwd that is an absolute path, the PATH |
| 150 | 154 | // should still be searched and the goodbye.exe in something should be found. |
| 151 | 155 | try testExecWithCwd(allocator, "goodbye", tmp_absolute_path, "hello from exe\n"); |
| 156 | ||
| 157 | // introduce some extra path separators into the path which is dealt with inside the spawn call. | |
| 158 | const denormed_something_subdir_size = std.mem.replacementSize(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\")); | |
| 159 | ||
| 160 | const denormed_something_subdir_abs_path = try allocator.allocSentinel(u16, denormed_something_subdir_size, 0); | |
| 161 | defer allocator.free(denormed_something_subdir_abs_path); | |
| 162 | ||
| 163 | _ = std.mem.replace(u16, something_subdir_abs_path, utf16Literal("\\"), utf16Literal("\\\\\\\\"), denormed_something_subdir_abs_path); | |
| 164 | ||
| 165 | const denormed_something_subdir_wtf8 = try std.unicode.wtf16LeToWtf8Alloc(allocator, denormed_something_subdir_abs_path); | |
| 166 | defer allocator.free(denormed_something_subdir_wtf8); | |
| 167 | ||
| 168 | // clear the path to ensure that the match comes from the cwd | |
| 169 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | |
| 170 | utf16Literal("PATH"), | |
| 171 | null, | |
| 172 | ) == windows.TRUE); | |
| 173 | ||
| 174 | try testExecWithCwd(allocator, "goodbye", denormed_something_subdir_wtf8, "hello from exe\n"); | |
| 175 | ||
| 176 | // normalization should also work if the non-normalized path is found in the PATH var. | |
| 177 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | |
| 178 | utf16Literal("PATH"), | |
| 179 | denormed_something_subdir_abs_path, | |
| 180 | ) == windows.TRUE); | |
| 181 | try testExec(allocator, "goodbye", "hello from exe\n"); | |
| 182 | ||
| 183 | // now make sure we can launch executables "outside" of the cwd | |
| 184 | var subdir_cwd = try tmp.dir.openDir(denormed_something_subdir_wtf8, .{}); | |
| 185 | defer subdir_cwd.close(); | |
| 186 | ||
| 187 | try tmp.dir.rename("something/goodbye.exe", "hello.exe"); | |
| 188 | try subdir_cwd.setAsCwd(); | |
| 189 | ||
| 190 | // clear the PATH again | |
| 191 | std.debug.assert(windows.kernel32.SetEnvironmentVariableW( | |
| 192 | utf16Literal("PATH"), | |
| 193 | null, | |
| 194 | ) == windows.TRUE); | |
| 195 | ||
| 196 | // while we're at it make sure non-windows separators work fine | |
| 197 | try testExec(allocator, "../hello", "hello from exe\n"); | |
| 152 | 198 | } |
| 153 | 199 | |
| 154 | 200 | fn testExecError(err: anyerror, allocator: std.mem.Allocator, command: []const u8) !void { |
test/tests.zig-9| ... | ... | @@ -2238,7 +2238,6 @@ const ModuleTestOptions = struct { |
| 2238 | 2238 | desc: []const u8, |
| 2239 | 2239 | optimize_modes: []const OptimizeMode, |
| 2240 | 2240 | include_paths: []const []const u8, |
| 2241 | windows_libs: []const []const u8, | |
| 2242 | 2241 | skip_single_threaded: bool, |
| 2243 | 2242 | skip_non_native: bool, |
| 2244 | 2243 | skip_freebsd: bool, |
| ... | ... | @@ -2373,10 +2372,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 2373 | 2372 | |
| 2374 | 2373 | for (options.include_paths) |include_path| these_tests.root_module.addIncludePath(b.path(include_path)); |
| 2375 | 2374 | |
| 2376 | if (target.os.tag == .windows) { | |
| 2377 | for (options.windows_libs) |lib| these_tests.root_module.linkSystemLibrary(lib, .{}); | |
| 2378 | } | |
| 2379 | ||
| 2380 | 2375 | const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{ |
| 2381 | 2376 | options.name, |
| 2382 | 2377 | triple_txt, |
| ... | ... | @@ -2672,10 +2667,6 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void { |
| 2672 | 2667 | }), |
| 2673 | 2668 | }); |
| 2674 | 2669 | |
| 2675 | if (b.graph.host.result.os.tag == .windows) { | |
| 2676 | incr_check.root_module.linkSystemLibrary("advapi32", .{}); | |
| 2677 | } | |
| 2678 | ||
| 2679 | 2670 | var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true }); |
| 2680 | 2671 | defer dir.close(); |
| 2681 | 2672 |