authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-06 22:52:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-06 22:52:19-07:00
logb9e1fef5628f01892cf09ade4425625a2cf34911
tree53c98c274d22bcd956ff5affa983ee80c6be9fb7
parent5667435bc4eacb36b4c33470784f1662637341b4
parentaac26f3b31ddb43e863ac7186fd19a7e251c1b8a

Merge remote-tracking branch 'origin/http' into wrangle-writer-buffering


87 files changed, 2544 insertions(+), 2345 deletions(-)

.github/workflows/ci.yaml+18
......@@ -50,6 +50,24 @@ jobs:
5050 uses: actions/checkout@v4
5151 - name: Build and Test
5252 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
5371 x86_64-macos-release:
5472 runs-on: "macos-13"
5573 env:
.github/workflows/riscv.yaml deleted-22
......@@ -1,22 +0,0 @@
1name: riscv
2on:
3 workflow_dispatch:
4permissions:
5 contents: read
6jobs:
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 {
9090 const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
9191 const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
9292 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;
9394 const skip_translate_c = b.option(bool, "skip-translate-c", "Main test suite skips translate-c tests") orelse false;
9495 const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;
9596 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 {
418419 try tests.addCases(b, test_cases_step, target, .{
419420 .test_filters = test_filters,
420421 .test_target_filters = test_target_filters,
422 .skip_compile_errors = skip_compile_errors,
421423 .skip_non_native = skip_non_native,
422424 .skip_freebsd = skip_freebsd,
423425 .skip_netbsd = skip_netbsd,
......@@ -450,7 +452,6 @@ pub fn build(b: *std.Build) !void {
450452 .desc = "Run the behavior tests",
451453 .optimize_modes = optimization_modes,
452454 .include_paths = &.{},
453 .windows_libs = &.{},
454455 .skip_single_threaded = skip_single_threaded,
455456 .skip_non_native = skip_non_native,
456457 .skip_freebsd = skip_freebsd,
......@@ -473,7 +474,6 @@ pub fn build(b: *std.Build) !void {
473474 .desc = "Run the @cImport tests",
474475 .optimize_modes = optimization_modes,
475476 .include_paths = &.{"test/c_import"},
476 .windows_libs = &.{},
477477 .skip_single_threaded = true,
478478 .skip_non_native = skip_non_native,
479479 .skip_freebsd = skip_freebsd,
......@@ -494,7 +494,6 @@ pub fn build(b: *std.Build) !void {
494494 .desc = "Run the compiler_rt tests",
495495 .optimize_modes = optimization_modes,
496496 .include_paths = &.{},
497 .windows_libs = &.{},
498497 .skip_single_threaded = true,
499498 .skip_non_native = skip_non_native,
500499 .skip_freebsd = skip_freebsd,
......@@ -516,7 +515,6 @@ pub fn build(b: *std.Build) !void {
516515 .desc = "Run the zigc tests",
517516 .optimize_modes = optimization_modes,
518517 .include_paths = &.{},
519 .windows_libs = &.{},
520518 .skip_single_threaded = true,
521519 .skip_non_native = skip_non_native,
522520 .skip_freebsd = skip_freebsd,
......@@ -538,12 +536,6 @@ pub fn build(b: *std.Build) !void {
538536 .desc = "Run the standard library tests",
539537 .optimize_modes = optimization_modes,
540538 .include_paths = &.{},
541 .windows_libs = &.{
542 "advapi32",
543 "crypt32",
544 "iphlpapi",
545 "ws2_32",
546 },
547539 .skip_single_threaded = skip_single_threaded,
548540 .skip_non_native = skip_non_native,
549541 .skip_freebsd = skip_freebsd,
......@@ -741,12 +733,6 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu
741733 compiler_mod.addImport("aro", aro_mod);
742734 compiler_mod.addImport("aro_translate_c", aro_translate_c_mod);
743735
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
750736 return compiler_mod;
751737}
752738
......@@ -1444,10 +1430,6 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath {
14441430 }),
14451431 });
14461432
1447 if (b.graph.host.result.os.tag == .windows) {
1448 doctest_exe.root_module.linkSystemLibrary("advapi32", .{});
1449 }
1450
14511433 var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
14521434 std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
14531435 b.build_root, @errorName(err),
ci/riscv64-linux-debug.sh+2-1
......@@ -49,11 +49,12 @@ unset CXX
4949ninja install
5050
5151# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
52stage3-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 \
52stage3-debug/bin/zig build test-cases test-modules test-unit test-c-abi test-stack-traces test-asm-link test-llvm-ir \
5353 --maxrss 68719476736 \
5454 -Dstatic-llvm \
5555 -Dskip-non-native \
5656 -Dskip-single-threaded \
57 -Dskip-compile-errors \
5758 -Dskip-translate-c \
5859 -Dskip-run-translated-c \
5960 -Dtarget=native-native-musl \
ci/riscv64-linux-release.sh+2-1
......@@ -49,11 +49,12 @@ unset CXX
4949ninja install
5050
5151# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
52stage3-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 \
52stage3-release/bin/zig build test-cases test-modules test-unit test-c-abi test-stack-traces test-asm-link test-llvm-ir \
5353 --maxrss 68719476736 \
5454 -Dstatic-llvm \
5555 -Dskip-non-native \
5656 -Dskip-single-threaded \
57 -Dskip-compile-errors \
5758 -Dskip-translate-c \
5859 -Dskip-run-translated-c \
5960 -Dtarget=native-native-musl \
doc/langref/test_switch_dispatch_loop.zig+5-3
......@@ -8,20 +8,22 @@ const Instruction = enum {
88};
99
1010fn 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);
1214 var ip: usize = 0;
1315
1416 return vm: switch (code[ip]) {
1517 // Because all code after `continue` is unreachable, this branch does
1618 // not provide a result.
1719 .add => {
18 try stack.append(stack.pop().? + stack.pop().?);
20 try stack.appendBounded(stack.pop().? + stack.pop().?);
1921
2022 ip += 1;
2123 continue :vm code[ip];
2224 },
2325 .mul => {
24 try stack.append(stack.pop().? * stack.pop().?);
26 try stack.appendBounded(stack.pop().? * stack.pop().?);
2527
2628 ip += 1;
2729 continue :vm code[ip];
lib/compiler/aro/aro/Attribute.zig+1-1
......@@ -708,7 +708,7 @@ pub const Arguments = blk: {
708708 field.* = .{
709709 .name = decl.name,
710710 .type = @field(attributes, decl.name),
711 .alignment = 0,
711 .alignment = @alignOf(@field(attributes, decl.name)),
712712 };
713713 }
714714
lib/compiler/build_runner.zig+4-1
......@@ -502,6 +502,9 @@ pub fn main() !void {
502502 };
503503 }
504504
505 // Comptime-known guard to prevent including the logic below when `!Watch.have_impl`.
506 if (!Watch.have_impl) unreachable;
507
505508 try w.update(gpa, run.step_stack.keys());
506509
507510 // Wait until a file system notification arrives. Read all such events
......@@ -511,7 +514,7 @@ pub fn main() !void {
511514 // recursive dependants.
512515 var caption_buf: [std.Progress.Node.max_name_len]u8 = undefined;
513516 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()),
515518 }) catch &caption_buf;
516519 var debouncing_node = main_progress_node.start(caption, 0);
517520 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
11411141 }
11421142 output_format = .res;
11431143 }
1144 } else {
1145 output_format_source = .output_format_arg;
11441146 }
11451147 options.output_source = .{ .filename = try filepathWithExtension(allocator, options.input_source.filename, output_format.?.extension()) };
11461148 } else {
......@@ -1529,21 +1531,21 @@ fn testParseOutput(args: []const []const u8, expected_output: []const u8) !?Opti
15291531 var diagnostics = Diagnostics.init(std.testing.allocator);
15301532 defer diagnostics.deinit();
15311533
1532 var output = std.ArrayList(u8).init(std.testing.allocator);
1534 var output: std.io.Writer.Allocating = .init(std.testing.allocator);
15331535 defer output.deinit();
15341536
15351537 var options = parse(std.testing.allocator, args, &diagnostics) catch |err| switch (err) {
15361538 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());
15391541 return null;
15401542 },
15411543 else => |e| return e,
15421544 };
15431545 errdefer options.deinit();
15441546
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());
15471549 return options;
15481550}
15491551
lib/compiler/resinator/compile.zig+50-46
......@@ -550,7 +550,7 @@ pub const Compiler = struct {
550550 // so get it here to simplify future usage.
551551 const filename_token = node.filename.getFirstToken();
552552
553 const file = self.searchForFile(filename_utf8) catch |err| switch (err) {
553 const file_handle = self.searchForFile(filename_utf8) catch |err| switch (err) {
554554 error.OutOfMemory => |e| return e,
555555 else => |e| {
556556 const filename_string_index = try self.diagnostics.putString(filename_utf8);
......@@ -564,13 +564,15 @@ pub const Compiler = struct {
564564 });
565565 },
566566 };
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);
568570
569571 if (maybe_predefined_type) |predefined_type| {
570572 switch (predefined_type) {
571573 .GROUP_ICON, .GROUP_CURSOR => {
572574 // Check for animated icon first
573 if (ani.isAnimatedIcon(file.deprecatedReader())) {
575 if (ani.isAnimatedIcon(file_reader.interface.adaptToOldInterface())) {
574576 // Animated icons are just put into the resource unmodified,
575577 // and the resource type changes to ANIICON/ANICURSOR
576578
......@@ -582,18 +584,18 @@ pub const Compiler = struct {
582584 header.type_value.ordinal = @intFromEnum(new_predefined_type);
583585 header.memory_flags = MemoryFlags.defaults(new_predefined_type);
584586 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());
586588
587589 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);
590592 return;
591593 }
592594
593595 // isAnimatedIcon moved the file cursor so reset to the start
594 try file.seekTo(0);
596 try file_reader.seekTo(0);
595597
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) {
597599 error.OutOfMemory => |e| return e,
598600 else => |e| {
599601 return self.iconReadError(
......@@ -671,15 +673,15 @@ pub const Compiler = struct {
671673 try writer.writeInt(u16, entry.type_specific_data.cursor.hotspot_y, .little);
672674 }
673675
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 {
676678 return self.iconReadError(
677679 error.UnexpectedEOF,
678680 filename_utf8,
679681 filename_token,
680682 predefined_type,
681683 );
682 };
684 }).*;
683685
684686 const image_format = ico.ImageFormat.detect(&header_bytes);
685687 if (!image_format.validate(&header_bytes)) {
......@@ -802,8 +804,8 @@ pub const Compiler = struct {
802804 },
803805 }
804806
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);
807809 try writeDataPadding(writer, full_data_size);
808810
809811 if (self.state.icon_id == std.math.maxInt(u16)) {
......@@ -857,9 +859,9 @@ pub const Compiler = struct {
857859 },
858860 .BITMAP => {
859861 header.applyMemoryFlags(node.common_resource_attributes, self.source);
860 const file_size = try file.getEndPos();
862 const file_size = try file_reader.getSize();
861863
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| {
863865 const filename_string_index = try self.diagnostics.putString(filename_utf8);
864866 return self.addErrorDetailsAndFail(.{
865867 .err = .bmp_read_error,
......@@ -921,18 +923,17 @@ pub const Compiler = struct {
921923
922924 header.data_size = bmp_bytes_to_write;
923925 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);
927928 if (bitmap_info.getBitmasksByteLen() > 0) {
928 try writeResourceDataNoPadding(writer, file_reader, bitmap_info.getBitmasksByteLen());
929 try writeResourceDataNoPadding(writer, &file_reader.interface, bitmap_info.getBitmasksByteLen());
929930 }
930931 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()));
932933 }
933 try file.seekTo(bitmap_info.pixel_data_offset);
934 try file_reader.seekTo(bitmap_info.pixel_data_offset);
934935 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);
936937 try writeDataPadding(writer, bmp_bytes_to_write);
937938 return;
938939 },
......@@ -956,7 +957,7 @@ pub const Compiler = struct {
956957 return;
957958 }
958959 header.applyMemoryFlags(node.common_resource_attributes, self.source);
959 const file_size = try file.getEndPos();
960 const file_size = try file_reader.getSize();
960961 if (file_size > std.math.maxInt(u32)) {
961962 return self.addErrorDetailsAndFail(.{
962963 .err = .resource_data_size_exceeds_max,
......@@ -968,8 +969,9 @@ pub const Compiler = struct {
968969 header.data_size = @intCast(file_size);
969970 try header.write(writer, self.errContext(node.id));
970971
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);
973975
974976 try self.state.font_dir.add(self.arena, FontDir.Font{
975977 .id = header.name_value.ordinal,
......@@ -992,7 +994,7 @@ pub const Compiler = struct {
992994 }
993995
994996 // 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();
996998 if (data_size > std.math.maxInt(u32)) {
997999 return self.addErrorDetailsAndFail(.{
9981000 .err = .resource_data_size_exceeds_max,
......@@ -1002,7 +1004,7 @@ pub const Compiler = struct {
10021004 // We now know that the data size will fit in a u32
10031005 header.data_size = @intCast(data_size);
10041006 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);
10061008 }
10071009
10081010 fn iconReadError(
......@@ -1250,8 +1252,8 @@ pub const Compiler = struct {
12501252 const data_len: u32 = @intCast(data_buffer.items.len);
12511253 try self.writeResourceHeader(writer, node.id, node.type, data_len, node.common_resource_attributes, self.state.language);
12521254
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);
12551257 }
12561258
12571259 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 {
12661268 try header.write(writer, self.errContext(id_token));
12671269 }
12681270
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();
12731277 }
12741278
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 {
12761280 try writeResourceDataNoPadding(writer, data_reader, data_size);
12771281 try writeDataPadding(writer, data_size);
12781282 }
......@@ -1337,8 +1341,8 @@ pub const Compiler = struct {
13371341
13381342 try header.write(writer, self.errContext(node.id));
13391343
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);
13421346 }
13431347
13441348 /// Expects `data_writer` to be a LimitedWriter limited to u32, meaning all writes to
......@@ -1730,8 +1734,8 @@ pub const Compiler = struct {
17301734
17311735 try header.write(writer, self.errContext(node.id));
17321736
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);
17351739 }
17361740
17371741 fn writeDialogHeaderAndStrings(
......@@ -2044,8 +2048,8 @@ pub const Compiler = struct {
20442048
20452049 try header.write(writer, self.errContext(node.id));
20462050
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);
20492053 }
20502054
20512055 /// Weight and italic carry over from previous FONT statements within a single resource,
......@@ -2119,8 +2123,8 @@ pub const Compiler = struct {
21192123
21202124 try header.write(writer, self.errContext(node.id));
21212125
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);
21242128 }
21252129
21262130 /// Expects `data_writer` to be a LimitedWriter limited to u32, meaning all writes to
......@@ -2384,8 +2388,8 @@ pub const Compiler = struct {
23842388
23852389 try header.write(writer, self.errContext(node.id));
23862390
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);
23892393 }
23902394
23912395 /// Expects writer to be a LimitedWriter limited to u16, meaning all writes to
......@@ -3319,8 +3323,8 @@ pub const StringTable = struct {
33193323 // we fully control and know are numbers, so they have a fixed size.
33203324 try header.writeAssertNoOverflow(writer);
33213325
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);
33243328 }
33253329 };
33263330
lib/compiler/resinator/cvtres.zig+24-29
......@@ -65,7 +65,7 @@ pub const ParseResOptions = struct {
6565};
6666
6767/// The returned ParsedResources should be freed by calling its `deinit` function.
68pub fn parseRes(allocator: Allocator, reader: anytype, options: ParseResOptions) !ParsedResources {
68pub fn parseRes(allocator: Allocator, reader: *std.Io.Reader, options: ParseResOptions) !ParsedResources {
6969 var resources = ParsedResources.init(allocator);
7070 errdefer resources.deinit();
7171
......@@ -74,7 +74,7 @@ pub fn parseRes(allocator: Allocator, reader: anytype, options: ParseResOptions)
7474 return resources;
7575}
7676
77pub fn parseResInto(resources: *ParsedResources, reader: anytype, options: ParseResOptions) !void {
77pub fn parseResInto(resources: *ParsedResources, reader: *std.Io.Reader, options: ParseResOptions) !void {
7878 const allocator = resources.allocator;
7979 var bytes_remaining: u64 = options.max_size;
8080 {
......@@ -103,43 +103,38 @@ pub const ResourceAndSize = struct {
103103 total_size: u64,
104104};
105105
106pub 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);
106pub 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);
111109 const total_size: u64 = @as(u64, header_size) + data_size;
112110 if (total_size > max_size) return error.ImpossibleSize;
113111
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);
117115 errdefer type_value.deinit(allocator);
118116
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);
122118 errdefer name_value.deinit(allocator);
123119
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);
126122
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);
133129
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;
136131
137132 const data = try allocator.alloc(u8, data_size);
138133 errdefer allocator.free(data);
139 try reader.readNoEof(data);
134 try reader.readSliceAll(data);
140135
141136 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);
143138
144139 return .{
145140 .resource = .{
......@@ -156,10 +151,10 @@ pub fn parseResource(allocator: Allocator, reader: anytype, max_size: u64) !Reso
156151 };
157152}
158153
159pub fn parseNameOrOrdinal(allocator: Allocator, reader: anytype) !NameOrOrdinal {
160 const first_code_unit = try reader.readInt(u16, .little);
154pub fn parseNameOrOrdinal(allocator: Allocator, reader: *std.Io.Reader) !NameOrOrdinal {
155 const first_code_unit = try reader.takeInt(u16, .little);
161156 if (first_code_unit == 0xFFFF) {
162 const ordinal_value = try reader.readInt(u16, .little);
157 const ordinal_value = try reader.takeInt(u16, .little);
163158 return .{ .ordinal = ordinal_value };
164159 }
165160 var name_buf = try std.ArrayListUnmanaged(u16).initCapacity(allocator, 16);
......@@ -167,7 +162,7 @@ pub fn parseNameOrOrdinal(allocator: Allocator, reader: anytype) !NameOrOrdinal
167162 var code_unit = first_code_unit;
168163 while (code_unit != 0) {
169164 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);
171166 }
172167 return .{ .name = try name_buf.toOwnedSliceSentinel(allocator, 0) };
173168}
lib/compiler/resinator/errors.zig+2-2
......@@ -1112,7 +1112,7 @@ const CorrespondingLines = struct {
11121112
11131113 try corresponding_lines.writeLineFromStreamVerbatim(
11141114 writer,
1115 corresponding_lines.buffered_reader.reader(),
1115 corresponding_lines.buffered_reader.interface.adaptToOldInterface(),
11161116 corresponding_span.start_line,
11171117 );
11181118
......@@ -1155,7 +1155,7 @@ const CorrespondingLines = struct {
11551155
11561156 try self.writeLineFromStreamVerbatim(
11571157 writer,
1158 self.buffered_reader.reader(),
1158 self.buffered_reader.interface.adaptToOldInterface(),
11591159 self.line_num,
11601160 );
11611161
lib/compiler/resinator/ico.zig+2-1
......@@ -14,8 +14,9 @@ pub fn read(allocator: std.mem.Allocator, reader: anytype, max_size: u64) ReadEr
1414 // Some Reader implementations have an empty ReadError error set which would
1515 // cause 'unreachable else' if we tried to use an else in the switch, so we
1616 // need to detect this case and not try to translate to ReadError
17 const anyerror_reader_errorset = @TypeOf(reader).Error == anyerror;
1718 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) {
1920 return readAnyError(allocator, reader, max_size) catch |err| switch (err) {
2021 error.EndOfStream => error.UnexpectedEOF,
2122 else => |e| return e,
lib/compiler/resinator/main.zig+2-2
......@@ -325,8 +325,8 @@ pub fn main() !void {
325325 std.debug.assert(options.output_format == .coff);
326326
327327 // 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| {
330330 // TODO: Better errors
331331 try error_handler.emitMessage(allocator, .err, "unable to parse res from '{s}': {s}", .{ res_stream.name, @errorName(err) });
332332 std.process.exit(1);
lib/docs/main.js+13-3
......@@ -129,6 +129,11 @@
129129 domSearch.addEventListener('input', onSearchChange, false);
130130 window.addEventListener('keydown', onWindowKeyDown, false);
131131 onHashChange(null);
132 if (domSearch.value) {
133 // user started typing a search query while the page was loading
134 curSearchIndex = -1;
135 startAsyncSearch();
136 }
132137 });
133138 });
134139
......@@ -643,6 +648,7 @@
643648 }
644649
645650 function onHashChange(state) {
651 // Use a non-null state value to prevent the window scrolling if the user goes back to this history entry.
646652 history.replaceState({}, "");
647653 navigate(location.hash);
648654 if (state == null) window.scrollTo({top: 0});
......@@ -650,13 +656,11 @@
650656
651657 function onPopState(ev) {
652658 onHashChange(ev.state);
659 syncDomSearch();
653660 }
654661
655662 function navigate(location_hash) {
656663 updateCurNav(location_hash);
657 if (domSearch.value !== curNavSearch) {
658 domSearch.value = curNavSearch;
659 }
660664 render();
661665 if (imFeelingLucky) {
662666 imFeelingLucky = false;
......@@ -664,6 +668,12 @@
664668 }
665669 }
666670
671 function syncDomSearch() {
672 if (domSearch.value !== curNavSearch) {
673 domSearch.value = curNavSearch;
674 }
675 }
676
667677 function activateSelectedResult() {
668678 if (domSectSearchResults.classList.contains("hidden")) {
669679 return;
lib/docs/wasm/markdown.zig+6-7
......@@ -143,13 +143,12 @@ fn mainImpl() !void {
143143 var parser = try Parser.init(gpa);
144144 defer parser.deinit();
145145
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);
153152 } else |err| switch (err) {
154153 error.EndOfStream => {},
155154 else => |e| return e,
lib/docs/wasm/markdown/Parser.zig+40-26
......@@ -29,13 +29,14 @@ const Node = Document.Node;
2929const ExtraIndex = Document.ExtraIndex;
3030const ExtraData = Document.ExtraData;
3131const StringIndex = Document.StringIndex;
32const ArrayList = std.ArrayListUnmanaged;
3233
3334nodes: Node.List = .{},
34extra: std.ArrayListUnmanaged(u32) = .empty,
35scratch_extra: std.ArrayListUnmanaged(u32) = .empty,
36string_bytes: std.ArrayListUnmanaged(u8) = .empty,
37scratch_string: std.ArrayListUnmanaged(u8) = .empty,
38pending_blocks: std.ArrayListUnmanaged(Block) = .empty,
35extra: ArrayList(u32) = .empty,
36scratch_extra: ArrayList(u32) = .empty,
37string_bytes: ArrayList(u8) = .empty,
38scratch_string: ArrayList(u8) = .empty,
39pending_blocks: ArrayList(Block) = .empty,
3940allocator: Allocator,
4041
4142const Parser = @This();
......@@ -86,7 +87,8 @@ const Block = struct {
8687 continuation_indent: usize,
8788 },
8889 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,
9092 },
9193 heading: struct {
9294 /// Between 1 and 6, inclusive.
......@@ -354,7 +356,8 @@ const BlockStart = struct {
354356 continuation_indent: usize,
355357 },
356358 table_row: struct {
357 cells: std.BoundedArray([]const u8, max_table_columns),
359 cells_buffer: [max_table_columns][]const u8,
360 cells_len: usize,
358361 },
359362 heading: struct {
360363 /// Between 1 and 6, inclusive.
......@@ -422,7 +425,8 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void {
422425 try p.pending_blocks.append(p.allocator, .{
423426 .tag = .table,
424427 .data = .{ .table = .{
425 .column_alignments = .{},
428 .column_alignments_buffer = undefined,
429 .column_alignments_len = 0,
426430 } },
427431 .string_start = p.scratch_string.items.len,
428432 .extra_start = p.scratch_extra.items.len,
......@@ -431,15 +435,19 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void {
431435
432436 const current_row = p.scratch_extra.items.len - p.pending_blocks.getLast().extra_start;
433437 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;
436444 if (current_row == 1) {
437445 // We need to go back and mark the header row and its column
438446 // alignments.
439447 const datas = p.nodes.items(.data);
440448 const header_data = datas[p.scratch_extra.getLast()];
441449 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;
443451 const cell_data = &datas[@intFromEnum(header_cell)].table_cell;
444452 cell_data.info.alignment = alignment;
445453 cell_data.info.header = true;
......@@ -480,8 +488,10 @@ fn appendBlockStart(p: *Parser, block_start: BlockStart) !void {
480488 // available in the BlockStart. We can immediately parse and append
481489 // these children now.
482490 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| {
485495 const cell_children = try p.parseInlines(cell_content);
486496 const alignment = if (i < column_alignments.len) column_alignments[i] else .unset;
487497 const cell = try p.addNode(.{
......@@ -523,7 +533,8 @@ fn startBlock(p: *Parser, line: []const u8) !?BlockStart {
523533 return .{
524534 .tag = .table_row,
525535 .data = .{ .table_row = .{
526 .cells = table_row.cells,
536 .cells_buffer = table_row.cells_buffer,
537 .cells_len = table_row.cells_len,
527538 } },
528539 .rest = "",
529540 };
......@@ -606,7 +617,8 @@ fn startListItem(unindented_line: []const u8) ?ListItemStart {
606617}
607618
608619const TableRowStart = struct {
609 cells: std.BoundedArray([]const u8, max_table_columns),
620 cells_buffer: [max_table_columns][]const u8,
621 cells_len: usize,
610622};
611623
612624fn startTableRow(unindented_line: []const u8) ?TableRowStart {
......@@ -615,7 +627,8 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart {
615627 mem.endsWith(u8, unindented_line, "\\|") or
616628 !mem.endsWith(u8, unindented_line, "|")) return null;
617629
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);
619632 const table_row_content = unindented_line[1 .. unindented_line.len - 1];
620633 var cell_start: usize = 0;
621634 var i: usize = 0;
......@@ -623,7 +636,7 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart {
623636 switch (table_row_content[i]) {
624637 '\\' => i += 1,
625638 '|' => {
626 cells.append(table_row_content[cell_start..i]) catch return null;
639 cells.appendBounded(table_row_content[cell_start..i]) catch return null;
627640 cell_start = i + 1;
628641 },
629642 '`' => {
......@@ -641,20 +654,21 @@ fn startTableRow(unindented_line: []const u8) ?TableRowStart {
641654 else => {},
642655 }
643656 }
644 cells.append(table_row_content[cell_start..]) catch return null;
657 cells.appendBounded(table_row_content[cell_start..]) catch return null;
645658
646 return .{ .cells = cells };
659 return .{ .cells_buffer = cells_buffer, .cells_len = cells.items.len };
647660}
648661
649662fn 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| {
654668 const alignment = parseTableHeaderDelimiterCell(content) orelse return null;
655669 alignments.appendAssumeCapacity(alignment);
656670 }
657 return alignments;
671 return alignments.items;
658672}
659673
660674fn parseTableHeaderDelimiterCell(content: []const u8) ?Node.TableCellAlignment {
......@@ -928,8 +942,8 @@ const InlineParser = struct {
928942 parent: *Parser,
929943 content: []const u8,
930944 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,
933947
934948 const PendingInline = struct {
935949 tag: Tag,
lib/std/Build/Fuzz.zig+16-23
......@@ -234,7 +234,7 @@ pub const Previous = struct {
234234};
235235pub fn sendUpdate(
236236 fuzz: *Fuzz,
237 socket: *std.http.WebSocket,
237 socket: *std.http.Server.WebSocket,
238238 prev: *Previous,
239239) !void {
240240 fuzz.coverage_mutex.lock();
......@@ -263,36 +263,36 @@ pub fn sendUpdate(
263263 .string_bytes_len = @intCast(coverage_map.coverage.string_bytes.items.len),
264264 .start_timestamp = coverage_map.start_timestamp,
265265 };
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,
272272 };
273 try socket.writeMessagev(&iovecs, .binary);
273 try socket.writeMessageVec(&iovecs, .binary);
274274 }
275275
276276 const header: abi.CoverageUpdateHeader = .{
277277 .n_runs = n_runs,
278278 .unique_runs = unique_runs,
279279 };
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),
283283 };
284 try socket.writeMessagev(&iovecs, .binary);
284 try socket.writeMessageVec(&iovecs, .binary);
285285
286286 prev.unique_runs = unique_runs;
287287 }
288288
289289 if (prev.entry_points != coverage_map.entry_points.items.len) {
290290 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),
294294 };
295 try socket.writeMessagev(&iovecs, .binary);
295 try socket.writeMessageVec(&iovecs, .binary);
296296
297297 prev.entry_points = coverage_map.entry_points.items.len;
298298 }
......@@ -448,10 +448,3 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
448448 }
449449 try coverage_map.entry_points.append(fuzz.ws.gpa, @intCast(index));
450450}
451
452fn 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 {
18511851 const maybe_output_dir = step.evalZigProcess(
18521852 zig_args,
18531853 options.progress_node,
1854 (b.graph.incremental == true) and options.watch,
1854 (b.graph.incremental == true) and (options.watch or options.web_server != null),
18551855 options.web_server,
18561856 options.gpa,
18571857 ) catch |err| switch (err) {
lib/std/Build/Watch.zig+36-3
......@@ -1,13 +1,18 @@
11const builtin = @import("builtin");
22const std = @import("../std.zig");
3const Watch = @This();
43const Step = std.Build.Step;
54const Allocator = std.mem.Allocator;
65const assert = std.debug.assert;
76const fatal = std.process.fatal;
7const Watch = @This();
8const FsEvents = @import("Watch/FsEvents.zig");
89
9dir_table: DirTable,
1010os: Os,
11/// The number to show as the number of directories being watched.
12dir_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.
15dir_table: DirTable,
1116generation: Generation,
1217
1318pub const have_impl = Os != void;
......@@ -97,6 +102,7 @@ const Os = switch (builtin.os.tag) {
97102 fn init() !Watch {
98103 return .{
99104 .dir_table = .{},
105 .dir_count = 0,
100106 .os = switch (builtin.os.tag) {
101107 .linux => .{
102108 .handle_table = .{},
......@@ -273,6 +279,7 @@ const Os = switch (builtin.os.tag) {
273279 }
274280 w.generation +%= 1;
275281 }
282 w.dir_count = w.dir_table.count();
276283 }
277284
278285 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
......@@ -408,6 +415,7 @@ const Os = switch (builtin.os.tag) {
408415 fn init() !Watch {
409416 return .{
410417 .dir_table = .{},
418 .dir_count = 0,
411419 .os = switch (builtin.os.tag) {
412420 .windows => .{
413421 .handle_table = .{},
......@@ -572,6 +580,7 @@ const Os = switch (builtin.os.tag) {
572580 }
573581 w.generation +%= 1;
574582 }
583 w.dir_count = w.dir_table.count();
575584 }
576585
577586 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
......@@ -605,7 +614,7 @@ const Os = switch (builtin.os.tag) {
605614 };
606615 }
607616 },
608 .dragonfly, .freebsd, .netbsd, .openbsd, .ios, .macos, .tvos, .visionos, .watchos => struct {
617 .dragonfly, .freebsd, .netbsd, .openbsd, .ios, .tvos, .visionos, .watchos => struct {
609618 const posix = std.posix;
610619
611620 kq_fd: i32,
......@@ -639,6 +648,7 @@ const Os = switch (builtin.os.tag) {
639648 errdefer posix.close(kq_fd);
640649 return .{
641650 .dir_table = .{},
651 .dir_count = 0,
642652 .os = .{
643653 .kq_fd = kq_fd,
644654 .handles = .empty,
......@@ -769,6 +779,7 @@ const Os = switch (builtin.os.tag) {
769779 }
770780 w.generation +%= 1;
771781 }
782 w.dir_count = w.dir_table.count();
772783 }
773784
774785 fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
......@@ -812,6 +823,28 @@ const Os = switch (builtin.os.tag) {
812823 return any_dirty;
813824 }
814825 },
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 },
815848 else => void,
816849};
817850
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
21const enable_debug_logs = false;
22
23core_services: std.DynLib,
24resolved_symbols: ResolvedSymbols,
25
26paths_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.
30watch_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.
33watch_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.
37waiting_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.
41dispatch_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.
44since_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.
48const 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
81pub 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
105pub 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
118pub 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
207pub 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
286const 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
320const EventCallbackCtx = struct {
321 fse: *FsEvents,
322 gpa: Allocator,
323};
324
325fn 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}
380fn 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}
385fn 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
392const dispatch_time_t = enum(u64) {
393 now = 0,
394 forever = std.math.maxInt(u64),
395 _,
396};
397extern fn dispatch_time(base: dispatch_time_t, delta_ns: i64) dispatch_time_t;
398
399const dispatch_semaphore_t = *opaque {};
400extern fn dispatch_semaphore_create(value: isize) dispatch_semaphore_t;
401extern fn dispatch_semaphore_wait(dsema: dispatch_semaphore_t, timeout: dispatch_time_t) isize;
402extern fn dispatch_semaphore_signal(dsema: dispatch_semaphore_t) isize;
403
404const dispatch_queue_t = *opaque {};
405const dispatch_queue_attr_t = ?*opaque {
406 const SERIAL: dispatch_queue_attr_t = null;
407};
408extern fn dispatch_queue_create(label: [*:0]const u8, attr: dispatch_queue_attr_t) dispatch_queue_t;
409extern fn dispatch_release(object: *anyopaque) void;
410
411const CFAllocatorRef = ?*const opaque {};
412const CFArrayRef = *const opaque {};
413const CFStringRef = *const opaque {};
414const CFTimeInterval = f64;
415const CFIndex = i32;
416const CFOptionFlags = enum(u32) { _ };
417const CFAllocatorRetainCallBack = *const fn (info: ?*const anyopaque) callconv(.c) *const anyopaque;
418const CFAllocatorReleaseCallBack = *const fn (info: ?*const anyopaque) callconv(.c) void;
419const CFAllocatorCopyDescriptionCallBack = *const fn (info: ?*const anyopaque) callconv(.c) CFStringRef;
420const CFAllocatorAllocateCallBack = *const fn (alloc_size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) ?*const anyopaque;
421const CFAllocatorReallocateCallBack = *const fn (ptr: ?*anyopaque, new_size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) ?*const anyopaque;
422const CFAllocatorDeallocateCallBack = *const fn (ptr: *anyopaque, info: ?*const anyopaque) callconv(.c) void;
423const CFAllocatorPreferredSizeCallBack = *const fn (size: CFIndex, hint: CFOptionFlags, info: ?*const anyopaque) callconv(.c) CFIndex;
424const 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};
435const CFArrayCallBacks = opaque {};
436const 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
448const FSEventStreamRef = *opaque {};
449const ConstFSEventStreamRef = *const @typeInfo(FSEventStreamRef).pointer.child;
450const 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;
458const FSEventStreamContext = extern struct {
459 version: CFIndex,
460 info: ?*anyopaque,
461 retain: ?CFAllocatorRetainCallBack,
462 release: ?CFAllocatorReleaseCallBack,
463 copy_description: ?CFAllocatorCopyDescriptionCallBack,
464};
465const FSEventStreamEventId = enum(u64) {
466 since_now = std.math.maxInt(u64),
467 _,
468};
469const 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};
477const 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
489const std = @import("std");
490const assert = std.debug.assert;
491const Allocator = std.mem.Allocator;
492const watch_log = std.log.scoped(.watch);
493const FsEvents = @This();
lib/std/Build/WebServer.zig+42-53
......@@ -251,48 +251,44 @@ pub fn now(s: *const WebServer) i64 {
251251fn accept(ws: *WebServer, connection: std.net.Server.Connection) void {
252252 defer connection.stream.close();
253253
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);
256259
257260 while (true) {
258261 var request = server.receiveHead() catch |err| switch (err) {
259262 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}),
264264 };
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});
282273 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 },
285287 }
286288 }
287289}
288290
289fn makeIov(s: []const u8) std.posix.iovec_const {
290 return .{
291 .base = s.ptr,
292 .len = s.len,
293 };
294}
295fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn {
291fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
296292 var prev_build_status = ws.build_status.load(.monotonic);
297293
298294 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 {
312308 .timestamp = ws.now(),
313309 .steps_len = @intCast(ws.all_steps.len),
314310 };
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);
320313 }
321314
322315 var prev_fuzz: Fuzz.Previous = .init;
......@@ -380,7 +373,7 @@ fn serveWebSocket(ws: *WebServer, sock: *std.http.WebSocket) !noreturn {
380373 std.Thread.Futex.timedWait(&ws.update_id, start_update_id, std.time.ns_per_ms * default_update_interval_ms) catch {};
381374 }
382375}
383fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void {
376fn recvWebSocketMessages(ws: *WebServer, sock: *http.Server.WebSocket) void {
384377 while (true) {
385378 const msg = sock.readSmallMessage() catch return;
386379 if (msg.opcode != .binary) continue;
......@@ -402,7 +395,7 @@ fn recvWebSocketMessages(ws: *WebServer, sock: *std.http.WebSocket) void {
402395 }
403396}
404397
405fn serveRequest(ws: *WebServer, req: *std.http.Server.Request) !void {
398fn serveRequest(ws: *WebServer, req: *http.Server.Request) !void {
406399 // Strip an optional leading '/debug' component from the request.
407400 const target: []const u8, const debug: bool = target: {
408401 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 {
431424
432425fn serveLibFile(
433426 ws: *WebServer,
434 request: *std.http.Server.Request,
427 request: *http.Server.Request,
435428 sub_path: []const u8,
436429 content_type: []const u8,
437430) !void {
......@@ -442,7 +435,7 @@ fn serveLibFile(
442435}
443436fn serveClientWasm(
444437 ws: *WebServer,
445 req: *std.http.Server.Request,
438 req: *http.Server.Request,
446439 optimize_mode: std.builtin.OptimizeMode,
447440) !void {
448441 var arena_state: std.heap.ArenaAllocator = .init(ws.gpa);
......@@ -456,12 +449,12 @@ fn serveClientWasm(
456449
457450pub fn serveFile(
458451 ws: *WebServer,
459 request: *std.http.Server.Request,
452 request: *http.Server.Request,
460453 path: Cache.Path,
461454 content_type: []const u8,
462455) !void {
463456 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.
465458 // We load the file with every request so that the user can make changes to the file
466459 // and refresh the HTML page without restarting this server.
467460 const file_contents = path.root_dir.handle.readFileAlloc(gpa, path.sub_path, 10 * 1024 * 1024) catch |err| {
......@@ -478,14 +471,13 @@ pub fn serveFile(
478471}
479472pub fn serveTarFile(
480473 ws: *WebServer,
481 request: *std.http.Server.Request,
474 request: *http.Server.Request,
482475 paths: []const Cache.Path,
483476) !void {
484477 const gpa = ws.gpa;
485478
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, .{
489481 .respond_options = .{
490482 .extra_headers = &.{
491483 .{ .name = "Content-Type", .value = "application/x-tar" },
......@@ -497,10 +489,7 @@ pub fn serveTarFile(
497489 var cached_cwd_path: ?[]const u8 = null;
498490 defer if (cached_cwd_path) |p| gpa.free(p);
499491
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 };
504493
505494 for (paths) |path| {
506495 var file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| {
......@@ -526,7 +515,6 @@ pub fn serveTarFile(
526515 }
527516
528517 // intentionally not calling `archiver.finishPedantically`
529 try adapter.new_interface.flush();
530518 try response.end();
531519}
532520
......@@ -804,7 +792,7 @@ pub fn wait(ws: *WebServer) RunnerRequest {
804792 }
805793}
806794
807const cache_control_header: std.http.Header = .{
795const cache_control_header: http.Header = .{
808796 .name = "Cache-Control",
809797 .value = "max-age=0, must-revalidate",
810798};
......@@ -819,5 +807,6 @@ const Build = std.Build;
819807const Cache = Build.Cache;
820808const Fuzz = Build.Fuzz;
821809const abi = Build.abi;
810const http = std.http;
822811
823812const WebServer = @This();
lib/std/Io/DeprecatedReader.zig-27
......@@ -249,33 +249,6 @@ pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes
249249 return bytes;
250250}
251251
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()`
256pub 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.
273pub 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
279252pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
280253 const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8));
281254 return mem.readInt(T, &bytes, endian);
lib/std/Io/Reader.zig+105-87
......@@ -70,13 +70,14 @@ pub const VTable = struct {
7070
7171 /// Returns number of bytes written to `data`.
7272 ///
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`.
7475 ///
7576 /// `data` may not contain an alias to `Reader.buffer`.
7677 ///
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.
8081 ///
8182 /// Implementations may ignore `data`, writing directly to `Reader.buffer`,
8283 /// modifying `seek` and `end` accordingly, and returning 0 from this
......@@ -366,8 +367,11 @@ pub fn appendRemainingUnlimited(
366367 const buffer_contents = r.buffer[r.seek..r.end];
367368 try list.ensureUnusedCapacity(gpa, buffer_contents.len + bump);
368369 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 }
371375 // From here, we leave `buffer` empty, appending directly to `list`.
372376 var writer: Writer = .{
373377 .buffer = undefined,
......@@ -421,23 +425,29 @@ pub fn readVec(r: *Reader, data: [][]u8) Error!usize {
421425
422426/// Writes to `Reader.buffer` or `data`, whichever has larger capacity.
423427pub fn defaultReadVec(r: *Reader, data: [][]u8) Error!usize {
424 assert(r.seek == r.end);
425 r.seek = 0;
426 r.end = 0;
427428 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 }
429441 var writer: Writer = .{
430 .buffer = if (direct) first else r.buffer,
431 .end = 0,
442 .buffer = r.buffer,
443 .end = r.end,
432444 .vtable = &.{ .drain = Writer.fixedDrain },
433445 };
434446 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) {
436448 error.WriteFailed => unreachable,
437449 else => |e| return e,
438450 };
439 if (direct) return n;
440 r.end += n;
441451 return 0;
442452}
443453
......@@ -1059,17 +1069,8 @@ pub fn fill(r: *Reader, n: usize) Error!void {
10591069/// increasing by a factor of 5 or more.
10601070fn fillUnbuffered(r: *Reader, n: usize) Error!void {
10611071 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);
10731074}
10741075
10751076/// 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 {
10791080/// Asserts buffer capacity is at least 1.
10801081pub fn fillMore(r: *Reader) Error!void {
10811082 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);
10911085}
10921086
10931087/// Returns the next byte from the stream or returns `error.EndOfStream`.
......@@ -1315,31 +1309,6 @@ pub fn defaultRebase(r: *Reader, capacity: usize) RebaseError!void {
13151309 r.end = data.len;
13161310}
13171311
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.
1326pub 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.
1337pub 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
13431312test fixed {
13441313 var r: Reader = .fixed("a\x02");
13451314 try testing.expect((try r.takeByte()) == 'a');
......@@ -1796,18 +1765,26 @@ pub fn Hashed(comptime Hasher: type) type {
17961765
17971766 fn readVec(r: *Reader, data: [][]u8) Error!usize {
17981767 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);
18001772 var remaining: usize = n;
1801 for (data) |slice| {
1773 for (dest) |slice| {
18021774 if (remaining < slice.len) {
18031775 this.hasher.update(slice[0..remaining]);
1804 return n;
1776 remaining = 0;
1777 break;
18051778 } else {
18061779 remaining -= slice.len;
18071780 this.hasher.update(slice);
18081781 }
18091782 }
18101783 assert(remaining == 0);
1784 if (n > data_size) {
1785 r.end += n - data_size;
1786 return data_size;
1787 }
18111788 return n;
18121789 }
18131790
......@@ -1824,17 +1801,24 @@ pub fn Hashed(comptime Hasher: type) type {
18241801pub fn writableVectorPosix(r: *Reader, buffer: []std.posix.iovec, data: []const []u8) Error!struct { usize, usize } {
18251802 var i: usize = 0;
18261803 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;
18291814 if (buf.len != 0) {
1815 r.seek = 0;
1816 r.end = 0;
18301817 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
18311818 i += 1;
1832 n += buf.len;
18331819 }
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..];
18381822 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
18391823 i += 1;
18401824 }
......@@ -1848,28 +1832,62 @@ pub fn writableVectorWsa(
18481832) Error!struct { usize, usize } {
18491833 var i: usize = 0;
18501834 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) };
18561846 i += 1;
1857 n += len;
1858 continue;
1847 n += std.math.maxInt(u32);
1848 return .{ i, n };
18591849 }
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 };
18611866 i += 1;
1862 n += std.math.maxInt(u32);
1863 return .{ i, n };
18641867 }
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
1871pub 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;
18721888 }
1889 } else {
1890 buffer[i] = r.buffer[r.end..];
18731891 i += 1;
18741892 }
18751893 return .{ i, n };
lib/std/Io/Writer.zig+77-19
......@@ -191,29 +191,87 @@ pub fn writeSplatHeader(
191191 data: []const []const u8,
192192 splat: usize,
193193) 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.
198pub 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;
208220 }
209221 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
238fn 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 }
212270 vecs[i] = pattern;
213271 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);
217275}
218276
219277test "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" {
4545 const expected_file_size: u64 = "begin".len + data.len + "end".len;
4646 try expectEqual(expected_file_size, file_size);
4747
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));
5151 defer std.testing.allocator.free(contents);
5252
5353 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
10061006 continue;
10071007 }
10081008 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);
10101010 m.remaining_read_trash_bytes = 0;
10111011 bytes_read = src.len;
10121012 continue;
lib/std/Target.zig+69-65
......@@ -405,7 +405,7 @@ pub const Os = struct {
405405 .fuchsia => .{
406406 .semver = .{
407407 .min = .{ .major = 1, .minor = 0, .patch = 0 },
408 .max = .{ .major = 26, .minor = 0, .patch = 0 },
408 .max = .{ .major = 27, .minor = 0, .patch = 0 },
409409 },
410410 },
411411 .hermit => .{
......@@ -446,7 +446,7 @@ pub const Os = struct {
446446
447447 break :blk default_min;
448448 },
449 .max = .{ .major = 6, .minor = 13, .patch = 4 },
449 .max = .{ .major = 6, .minor = 16, .patch = 0 },
450450 },
451451 .glibc = blk: {
452452 // For 32-bit targets that traditionally used 32-bit time, we require
......@@ -519,7 +519,7 @@ pub const Os = struct {
519519
520520 break :blk default_min;
521521 },
522 .max = .{ .major = 14, .minor = 2, .patch = 0 },
522 .max = .{ .major = 14, .minor = 3, .patch = 0 },
523523 },
524524 },
525525 .netbsd => .{
......@@ -549,38 +549,38 @@ pub const Os = struct {
549549
550550 .driverkit => .{
551551 .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 },
554554 },
555555 },
556556 .macos => .{
557557 .semver = .{
558558 .min = .{ .major = 13, .minor = 0, .patch = 0 },
559 .max = .{ .major = 15, .minor = 4, .patch = 1 },
559 .max = .{ .major = 15, .minor = 6, .patch = 0 },
560560 },
561561 },
562562 .ios => .{
563563 .semver = .{
564564 .min = .{ .major = 15, .minor = 0, .patch = 0 },
565 .max = .{ .major = 18, .minor = 4, .patch = 1 },
565 .max = .{ .major = 18, .minor = 6, .patch = 0 },
566566 },
567567 },
568568 .tvos => .{
569569 .semver = .{
570570 .min = .{ .major = 15, .minor = 0, .patch = 0 },
571 .max = .{ .major = 18, .minor = 4, .patch = 1 },
571 .max = .{ .major = 18, .minor = 5, .patch = 0 },
572572 },
573573 },
574574 .visionos => .{
575575 .semver = .{
576576 .min = .{ .major = 1, .minor = 0, .patch = 0 },
577 .max = .{ .major = 2, .minor = 4, .patch = 1 },
577 .max = .{ .major = 2, .minor = 5, .patch = 0 },
578578 },
579579 },
580580 .watchos => .{
581581 .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 },
584584 },
585585 },
586586
......@@ -614,7 +614,7 @@ pub const Os = struct {
614614 .amdhsa => .{
615615 .semver = .{
616616 .min = .{ .major = 5, .minor = 0, .patch = 0 },
617 .max = .{ .major = 6, .minor = 4, .patch = 0 },
617 .max = .{ .major = 6, .minor = 4, .patch = 2 },
618618 },
619619 },
620620 .amdpal => .{
......@@ -626,7 +626,7 @@ pub const Os = struct {
626626 .cuda => .{
627627 .semver = .{
628628 .min = .{ .major = 11, .minor = 0, .patch = 1 },
629 .max = .{ .major = 12, .minor = 9, .patch = 0 },
629 .max = .{ .major = 12, .minor = 9, .patch = 1 },
630630 },
631631 },
632632 .nvcl,
......@@ -646,7 +646,7 @@ pub const Os = struct {
646646 .vulkan => .{
647647 .semver = .{
648648 .min = .{ .major = 1, .minor = 2, .patch = 0 },
649 .max = .{ .major = 1, .minor = 4, .patch = 313 },
649 .max = .{ .major = 1, .minor = 4, .patch = 321 },
650650 },
651651 },
652652 };
......@@ -697,57 +697,6 @@ pub const Os = struct {
697697 => |field| @field(os.version_range, @tagName(field)).isAtLeast(ver),
698698 };
699699 }
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 }
751700};
752701
753702pub const aarch64 = @import("Target/aarch64.zig");
......@@ -2055,6 +2004,61 @@ pub inline fn isWasiLibC(target: *const Target) bool {
20552004 return target.os.tag == .wasi and target.abi.isMusl();
20562005}
20572006
2007/// Does this target require linking libc? This may be the case if the target has an unstable
2008/// syscall interface, for example.
2009pub 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
20582062pub const DynamicLinker = struct {
20592063 /// Contains the memory used to store the dynamic linker path. This field
20602064 /// 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 {
423423 try formatVersion(v, gpa, &result);
424424 },
425425 .windows => |v| {
426 try result.print(gpa, "{d}", .{v});
426 try result.print(gpa, "{f}", .{v});
427427 },
428428 }
429429 }
......@@ -437,7 +437,7 @@ pub fn zigTriple(self: Query, gpa: Allocator) Allocator.Error![]u8 {
437437 .windows => |v| {
438438 // This is counting on a custom format() function defined on `WindowsVersion`
439439 // 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});
441441 },
442442 }
443443 }
......@@ -729,4 +729,20 @@ test parse {
729729 defer std.testing.allocator.free(text);
730730 try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-android.30", text);
731731 }
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 }
732748}
lib/std/Uri.zig+2-1
......@@ -377,7 +377,8 @@ pub fn parse(text: []const u8) ParseError!Uri {
377377
378378pub const ResolveInPlaceError = ParseError || error{NoSpaceLeft};
379379
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)
381382///
382383/// Assumes new location is already copied to the beginning of `aux_buf.*`.
383384/// 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
158158 assert(self.items.len < self.capacity);
159159 self.items.len += 1;
160160
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]);
162162 self.items[i] = item;
163163 }
164164
......@@ -216,7 +216,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
216216 assert(self.capacity >= new_len);
217217 const to_move = self.items[index..];
218218 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);
220220 const result = self.items[index..][0..count];
221221 @memset(result, undefined);
222222 return result;
......@@ -624,6 +624,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
624624
625625 /// Initialize with externally-managed memory. The buffer determines the
626626 /// capacity, and the length is set to zero.
627 ///
627628 /// When initialized this way, all functions that accept an Allocator
628629 /// argument cause illegal behavior.
629630 pub fn initBuffer(buffer: Slice) Self {
......@@ -705,18 +706,37 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
705706 }
706707
707708 /// 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 ///
709712 /// This operation is O(N).
713 ///
710714 /// Asserts that the list has capacity for one additional item.
715 ///
711716 /// Asserts that the index is in bounds or equal to the length.
712717 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
713718 assert(self.items.len < self.capacity);
714719 self.items.len += 1;
715720
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]);
717722 self.items[i] = item;
718723 }
719724
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
720740 /// Add `count` new elements at position `index`, which have
721741 /// `undefined` values. Returns a slice pointing to the newly allocated
722742 /// elements, which becomes invalid after various `ArrayList`
......@@ -749,12 +769,29 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
749769 assert(self.capacity >= new_len);
750770 const to_move = self.items[index..];
751771 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);
753773 const result = self.items[index..][0..count];
754774 @memset(result, undefined);
755775 return result;
756776 }
757777
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
758795 /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room.
759796 /// This operation is O(N).
760797 /// Invalidates pre-existing pointers to elements at and after `index`.
......@@ -798,7 +835,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
798835 }
799836
800837 /// Grows or shrinks the list as necessary.
838 ///
801839 /// Never invalidates element pointers.
840 ///
802841 /// Asserts the capacity is enough for additional items.
803842 pub fn replaceRangeAssumeCapacity(self: *Self, start: usize, len: usize, new_items: []const T) void {
804843 const after_range = start + len;
......@@ -815,16 +854,24 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
815854 } else {
816855 const extra = range.len - new_items.len;
817856 @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);
823859 @memset(self.items[self.items.len - extra ..], undefined);
824860 self.items.len -= extra;
825861 }
826862 }
827863
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
828875 /// Extend the list by 1 element. Allocates more memory as necessary.
829876 /// Invalidates element pointers if additional memory is needed.
830877 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
833880 }
834881
835882 /// Extend the list by 1 element.
883 ///
836884 /// Never invalidates element pointers.
885 ///
837886 /// Asserts that the list can hold one additional item.
838887 pub fn appendAssumeCapacity(self: *Self, item: T) void {
839888 self.addOneAssumeCapacity().* = item;
840889 }
841890
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
842902 /// Remove the element at index `i` from the list and return its value.
843903 /// Invalidates pointers to the last element.
844904 /// This operation is O(N).
......@@ -873,6 +933,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
873933 }
874934
875935 /// Append the slice of items to the list.
936 ///
876937 /// Asserts that the list can hold the additional items.
877938 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
878939 const old_len = self.items.len;
......@@ -882,6 +943,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
882943 @memcpy(self.items[old_len..][0..items.len], items);
883944 }
884945
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
885954 /// Append the slice of items to the list. Allocates more
886955 /// memory as necessary. Only call this function if a call to `appendSlice` instead would
887956 /// be a compile error.
......@@ -892,8 +961,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
892961 }
893962
894963 /// 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 ///
897968 /// Asserts that the list can hold the additional items.
898969 pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void {
899970 const old_len = self.items.len;
......@@ -903,6 +974,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
903974 @memcpy(self.items[old_len..][0..items.len], items);
904975 }
905976
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
906989 pub fn print(self: *Self, gpa: Allocator, comptime fmt: []const u8, args: anytype) error{OutOfMemory}!void {
907990 comptime assert(T == u8);
908991 try self.ensureUnusedCapacity(gpa, fmt.len);
......@@ -920,6 +1003,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
9201003 self.items.len += w.end;
9211004 }
9221005
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
9231013 /// Append a value to the list `n` times.
9241014 /// Allocates more memory as necessary.
9251015 /// Invalidates element pointers if additional memory is needed.
......@@ -932,9 +1022,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
9321022 }
9331023
9341024 /// Append a value to the list `n` times.
1025 ///
9351026 /// Never invalidates element pointers.
1027 ///
9361028 /// The function is inline so that a comptime-known `value` parameter will
9371029 /// have better memset codegen in case it has a repeated byte pattern.
1030 ///
9381031 /// Asserts that the list can hold the additional items.
9391032 pub inline fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
9401033 const new_len = self.items.len + n;
......@@ -943,6 +1036,22 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
9431036 self.items.len = new_len;
9441037 }
9451038
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
9461055 /// Adjust the list length to `new_len`.
9471056 /// Additional elements contain the value `undefined`.
9481057 /// Invalidates element pointers if additional memory is needed.
......@@ -1068,8 +1177,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10681177 }
10691178
10701179 /// Increase length by 1, returning pointer to the new item.
1180 ///
10711181 /// Never invalidates element pointers.
1182 ///
10721183 /// The returned element pointer becomes invalid when the list is resized.
1184 ///
10731185 /// Asserts that the list can hold one additional item.
10741186 pub fn addOneAssumeCapacity(self: *Self) *T {
10751187 assert(self.items.len < self.capacity);
......@@ -1078,6 +1190,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10781190 return &self.items[self.items.len - 1];
10791191 }
10801192
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
10811205 /// Resize the array, adding `n` new elements, which have `undefined` values.
10821206 /// The return value is an array pointing to the newly allocated elements.
10831207 /// The returned pointer becomes invalid when the list is resized.
......@@ -1088,9 +1212,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10881212 }
10891213
10901214 /// Resize the array, adding `n` new elements, which have `undefined` values.
1215 ///
10911216 /// The return value is an array pointing to the newly allocated elements.
1217 ///
10921218 /// Never invalidates element pointers.
1219 ///
10931220 /// The returned pointer becomes invalid when the list is resized.
1221 ///
10941222 /// Asserts that the list can hold the additional items.
10951223 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
10961224 assert(self.items.len + n <= self.capacity);
......@@ -1099,6 +1227,21 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10991227 return self.items[prev_len..][0..n];
11001228 }
11011229
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
11021245 /// Resize the array, adding `n` new elements, which have `undefined` values.
11031246 /// The return value is a slice pointing to the newly allocated elements.
11041247 /// The returned pointer becomes invalid when the list is resized.
......@@ -1109,10 +1252,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11091252 return self.items[prev_len..][0..n];
11101253 }
11111254
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 ///
11161261 /// Asserts that the list can hold the additional items.
11171262 pub fn addManyAsSliceAssumeCapacity(self: *Self, n: usize) []T {
11181263 assert(self.items.len + n <= self.capacity);
......@@ -1121,6 +1266,19 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11211266 return self.items[prev_len..][0..n];
11221267 }
11231268
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
11241282 /// Remove and return the last element from the list.
11251283 /// If the list is empty, returns `null`.
11261284 /// Invalidates pointers to last element.
lib/std/base64.zig+6-26
......@@ -118,22 +118,6 @@ pub const Base64Encoder = struct {
118118 }
119119 }
120120
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
137121 /// dest.len must at least be what you get from ::calcSize.
138122 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {
139123 const out_len = encoder.calcSize(source.len);
......@@ -517,17 +501,13 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [
517501 var buffer: [0x100]u8 = undefined;
518502 const encoded = codecs.Encoder.encode(&buffer, expected_decoded);
519503 try testing.expectEqualSlices(u8, expected_encoded, encoded);
520
504 }
505 {
521506 // 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());
531511 }
532512
533513 // Base64Decoder
lib/std/bounded_array.zig deleted-412
......@@ -1,412 +0,0 @@
1const std = @import("std.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4const testing = std.testing;
5const 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/// ```
19pub 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/// ```
36pub 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
296test 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
402test "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) {
69706970 domainname: [256:0]u8,
69716971 },
69726972 .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,
69786978 },
69796979 // https://github.com/SerenityOS/serenity/blob/d794ed1de7a46482272683f8dc4c858806390f29/Kernel/API/POSIX/sys/utsname.h#L17-L23
69806980 .serenity => extern struct {
......@@ -6984,7 +6984,7 @@ pub const utsname = switch (native_os) {
69846984 version: [UTSNAME_ENTRY_LEN:0]u8,
69856985 machine: [UTSNAME_ENTRY_LEN:0]u8,
69866986
6987 const UTSNAME_ENTRY_LEN = 65;
6987 const UTSNAME_ENTRY_LEN = 64;
69886988 },
69896989 else => void,
69906990};
lib/std/compress/flate/Decompress.zig+4-3
......@@ -373,7 +373,7 @@ fn streamInner(d: *Decompress, w: *Writer, limit: std.Io.Limit) (Error || Reader
373373 d.state = .{ .stored_block = @intCast(remaining_len - n) };
374374 }
375375 w.advance(n);
376 return n;
376 return @intFromEnum(limit) - remaining + n;
377377 },
378378 .fixed_block => {
379379 while (remaining > 0) {
......@@ -603,7 +603,7 @@ fn tossBitsEnding(d: *Decompress, n: u4) !void {
603603 error.EndOfStream => unreachable,
604604 };
605605 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));
607607}
608608
609609fn takeBitsRuntime(d: *Decompress, n: u4) !u16 {
......@@ -1265,6 +1265,7 @@ fn testDecompress(container: Container, compressed: []const u8, expected_plain:
12651265 defer aw.deinit();
12661266
12671267 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);
12691270 try testing.expectEqualSlices(u8, expected_plain, aw.getWritten());
12701271}
lib/std/crypto/ecdsa.zig+1-1
......@@ -58,7 +58,7 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
5858 pub const PublicKey = struct {
5959 /// Length (in bytes) of a compressed sec1-encoded key.
6060 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.
6262 pub const uncompressed_sec1_encoded_length = 1 + 2 * Curve.Fe.encoded_length;
6363
6464 p: Curve,
lib/std/crypto/tls/Client.zig+51-19
......@@ -8,8 +8,8 @@ const mem = std.mem;
88const crypto = std.crypto;
99const assert = std.debug.assert;
1010const Certificate = std.crypto.Certificate;
11const Reader = std.io.Reader;
12const Writer = std.io.Writer;
11const Reader = std.Io.Reader;
12const Writer = std.Io.Writer;
1313
1414const max_ciphertext_len = tls.max_ciphertext_len;
1515const hmacExpandLabel = tls.hmacExpandLabel;
......@@ -27,6 +27,8 @@ reader: Reader,
2727
2828/// The encrypted stream from the client to the server. Bytes are pushed here
2929/// via `writer`.
30///
31/// The buffer is asserted to have capacity at least `min_buffer_len`.
3032output: *Writer,
3133/// The plaintext stream from the client to the server.
3234writer: Writer,
......@@ -122,7 +124,6 @@ pub const Options = struct {
122124 /// the amount of data expected, such as HTTP with the Content-Length header.
123125 allow_truncation_attacks: bool = false,
124126 write_buffer: []u8,
125 /// Asserted to have capacity at least `min_buffer_len`.
126127 read_buffer: []u8,
127128 /// Populated when `error.TlsAlert` is returned from `init`.
128129 alert: ?*tls.Alert = null,
......@@ -185,6 +186,7 @@ const InitError = error{
185186/// `input` is asserted to have buffer capacity at least `min_buffer_len`.
186187pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client {
187188 assert(input.buffer.len >= min_buffer_len);
189 assert(output.buffer.len >= min_buffer_len);
188190 const host = switch (options.host) {
189191 .no_verification => "",
190192 .explicit => |host| host,
......@@ -278,6 +280,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
278280 {
279281 var iovecs: [2][]const u8 = .{ cleartext_header, host };
280282 try output.writeVecAll(iovecs[0..if (host.len == 0) 1 else 2]);
283 try output.flush();
281284 }
282285
283286 var tls_version: tls.ProtocolVersion = undefined;
......@@ -328,7 +331,9 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
328331 var cleartext_bufs: [2][tls.max_ciphertext_inner_record_len]u8 = undefined;
329332 fragment: while (true) {
330333 // 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 };
332337 const record_header = input.peek(tls.record_header_len) catch |err| switch (err) {
333338 error.EndOfStream => return error.TlsConnectionTruncated,
334339 error.ReadFailed => return error.ReadFailed,
......@@ -761,6 +766,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
761766 &client_verify_msg,
762767 };
763768 try output.writeVecAll(&all_msgs_vec);
769 try output.flush();
764770 },
765771 }
766772 write_seq += 1;
......@@ -826,6 +832,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
826832 &finished_msg,
827833 };
828834 try output.writeVecAll(&all_msgs_vec);
835 try output.flush();
829836
830837 const client_secret = hkdfExpandLabel(P.Hkdf, pv.master_secret, "c ap traffic", &handshake_hash, P.Hash.digest_length);
831838 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
875882 .buffer = options.write_buffer,
876883 .vtable = &.{
877884 .drain = drain,
878 .sendFile = Writer.unimplementedSendFile,
885 .flush = flush,
879886 },
880887 },
881888 .tls_version = tls_version,
......@@ -908,32 +915,57 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
908915}
909916
910917fn 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));
914919 const output = c.output;
915920 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
916 var total_clear: usize = 0;
917921 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 }
923946 }
924947 output.advance(ciphertext_end);
925 return total_clear;
948 return w.consume(total_clear);
949}
950
951fn 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;
926958}
927959
928960/// Sends a `close_notify` alert, which is necessary for the server to
929961/// distinguish between a properly finished TLS session, or a truncation
930962/// attack.
931963pub fn end(c: *Client) Writer.Error!void {
964 try flush(&c.writer);
932965 const output = c.output;
933966 const ciphertext_buf = try output.writableSliceGreedy(min_buffer_len);
934967 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);
937969}
938970
939971fn prepareCiphertextRecord(
......@@ -1043,8 +1075,8 @@ pub fn eof(c: Client) bool {
10431075 return c.received_close_notify;
10441076}
10451077
1046fn stream(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
1047 const c: *Client = @fieldParentPtr("reader", r);
1078fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize {
1079 const c: *Client = @alignCast(@fieldParentPtr("reader", r));
10481080 if (c.eof()) return error.EndOfStream;
10491081 const input = c.input;
10501082 // 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 {
502502 };
503503 }
504504
505 pub fn iterateProgramHeadersBuffer(h: Header, buf: []const u8) ProgramHeaderBufferIterator {
506 return .{
507 .elf_header = h,
508 .buf = buf,
509 };
510 }
511
505512 pub fn iterateSectionHeaders(h: Header, file_reader: *std.fs.File.Reader) SectionHeaderIterator {
506513 return .{
507514 .elf_header = h,
......@@ -509,6 +516,13 @@ pub const Header = struct {
509516 };
510517 }
511518
519 pub fn iterateSectionHeadersBuffer(h: Header, buf: []const u8) SectionHeaderBufferIterator {
520 return .{
521 .elf_header = h,
522 .buf = buf,
523 };
524 }
525
512526 pub const ReadError = std.Io.Reader.Error || error{
513527 InvalidElfMagic,
514528 InvalidElfVersion,
......@@ -570,29 +584,48 @@ pub const ProgramHeaderIterator = struct {
570584 if (it.index >= it.elf_header.phnum) return null;
571585 defer it.index += 1;
572586
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;
581588 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
594pub 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);
593607 }
594608};
595609
610fn 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
596629pub const SectionHeaderIterator = struct {
597630 elf_header: Header,
598631 file_reader: *std.fs.File.Reader,
......@@ -602,29 +635,50 @@ pub const SectionHeaderIterator = struct {
602635 if (it.index >= it.elf_header.shnum) return null;
603636 defer it.index += 1;
604637
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);
610640
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
645pub 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);
625658 }
626659};
627660
661fn 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
628682pub const ELFCLASSNONE = 0;
629683pub const ELFCLASS32 = 1;
630684pub const ELFCLASS64 = 2;
lib/std/fs/File.zig+22-6
......@@ -1111,7 +1111,16 @@ pub const Reader = struct {
11111111 if (is_windows) {
11121112 // Unfortunately, `ReadFileScatter` cannot be used since it
11131113 // 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 }
11151124 }
11161125 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
11171126 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
......@@ -1141,8 +1150,7 @@ pub const Reader = struct {
11411150 }
11421151 r.pos += n;
11431152 if (n > data_size) {
1144 io_reader.seek = 0;
1145 io_reader.end = n - data_size;
1153 io_reader.end += n - data_size;
11461154 return data_size;
11471155 }
11481156 return n;
......@@ -1151,7 +1159,16 @@ pub const Reader = struct {
11511159 if (is_windows) {
11521160 // Unfortunately, `ReadFileScatter` cannot be used since it
11531161 // 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 }
11551172 }
11561173 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
11571174 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
......@@ -1167,8 +1184,7 @@ pub const Reader = struct {
11671184 }
11681185 r.pos += n;
11691186 if (n > data_size) {
1170 io_reader.seek = 0;
1171 io_reader.end = n - data_size;
1187 io_reader.end += n - data_size;
11721188 return data_size;
11731189 }
11741190 return n;
lib/std/http.zig+125-112
......@@ -1,7 +1,7 @@
11const builtin = @import("builtin");
22const std = @import("std.zig");
33const assert = std.debug.assert;
4const Writer = std.io.Writer;
4const Writer = std.Io.Writer;
55const File = std.fs.File;
66
77pub const Client = @import("http/Client.zig");
......@@ -20,51 +20,32 @@ pub const Version = enum {
2020/// https://datatracker.ietf.org/doc/html/rfc7231#section-4 Initial definition
2121///
2222/// https://datatracker.ietf.org/doc/html/rfc5789#section-2 PATCH
23pub 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 }
23pub const Method = enum {
24 GET,
25 HEAD,
26 POST,
27 PUT,
28 DELETE,
29 CONNECT,
30 OPTIONS,
31 TRACE,
32 PATCH,
5033
5134 /// Returns true if a request of this method is allowed to have a body
5235 /// 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) {
5538 .POST, .PUT, .PATCH => true,
5639 .GET, .HEAD, .DELETE, .CONNECT, .OPTIONS, .TRACE => false,
57 else => true,
5840 };
5941 }
6042
6143 /// Returns true if a response to this method is allowed to have a body
6244 /// 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) {
6547 .GET, .POST, .DELETE, .CONNECT, .OPTIONS, .PATCH => true,
6648 .HEAD, .PUT, .TRACE => false,
67 else => true,
6849 };
6950 }
7051
......@@ -73,11 +54,10 @@ pub const Method = enum(u64) {
7354 /// https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP
7455 ///
7556 /// 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) {
7859 .GET, .HEAD, .OPTIONS, .TRACE => true,
7960 .POST, .PUT, .DELETE, .CONNECT, .PATCH => false,
80 else => false,
8161 };
8262 }
8363
......@@ -88,11 +68,10 @@ pub const Method = enum(u64) {
8868 /// https://developer.mozilla.org/en-US/docs/Glossary/Idempotent
8969 ///
9070 /// 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) {
9373 .GET, .HEAD, .PUT, .DELETE, .OPTIONS, .TRACE => true,
9474 .CONNECT, .POST, .PATCH => false,
95 else => false,
9675 };
9776 }
9877
......@@ -102,11 +81,10 @@ pub const Method = enum(u64) {
10281 /// https://developer.mozilla.org/en-US/docs/Glossary/cacheable
10382 ///
10483 /// 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) {
10786 .GET, .HEAD => true,
10887 .POST, .PUT, .DELETE, .CONNECT, .OPTIONS, .TRACE, .PATCH => false,
109 else => false,
11088 };
11189 }
11290};
......@@ -327,11 +305,11 @@ pub const Header = struct {
327305};
328306
329307pub const Reader = struct {
330 in: *std.io.Reader,
308 in: *std.Io.Reader,
331309 /// This is preallocated memory that might be used by `bodyReader`. That
332310 /// 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,
335313 /// Keeps track of whether the stream is ready to accept a new request,
336314 /// making invalid API usage cause assertion failures rather than HTTP
337315 /// protocol violations.
......@@ -343,10 +321,6 @@ pub const Reader = struct {
343321 /// read from `in`.
344322 trailers: []const u8 = &.{},
345323 body_err: ?BodyError = null,
346 /// Stolen from `in`.
347 head_buffer: []u8 = &.{},
348
349 pub const max_chunk_header_len = 22;
350324
351325 pub const RemainingChunkLen = enum(u64) {
352326 head = 0,
......@@ -398,35 +372,34 @@ pub const Reader = struct {
398372 ReadFailed,
399373 };
400374
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 {
409380 reader.trailers = &.{};
410381 const in = reader.in;
411 in.restitute(reader.head_buffer.len);
412 reader.head_buffer.len = 0;
413 in.rebase();
414382 var hp: HeadParser = .{};
415 var head_end: usize = 0;
383 var head_len: usize = 0;
416384 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);
426398 if (hp.state == .finished) {
427 reader.head_buffer = in.steal(head_end);
428399 reader.state = .received_head;
429 return;
400 const head_buffer = in.buffered()[0..head_len];
401 in.toss(head_len);
402 return head_buffer;
430403 }
431404 }
432405 }
......@@ -442,7 +415,7 @@ pub const Reader = struct {
442415 buffer: []u8,
443416 transfer_encoding: TransferEncoding,
444417 content_length: ?u64,
445 ) *std.io.Reader {
418 ) *std.Io.Reader {
446419 assert(reader.state == .received_head);
447420 switch (transfer_encoding) {
448421 .chunked => {
......@@ -492,7 +465,7 @@ pub const Reader = struct {
492465 content_encoding: ContentEncoding,
493466 decompressor: *Decompressor,
494467 decompression_buffer: []u8,
495 ) *std.io.Reader {
468 ) *std.Io.Reader {
496469 if (transfer_encoding == .none and content_length == null) {
497470 assert(reader.state == .received_head);
498471 reader.state = .body_none;
......@@ -501,7 +474,7 @@ pub const Reader = struct {
501474 return reader.in;
502475 },
503476 .deflate => {
504 decompressor.* = .{ .flate = .init(reader.in, .raw, decompression_buffer) };
477 decompressor.* = .{ .flate = .init(reader.in, .zlib, decompression_buffer) };
505478 return &decompressor.flate.reader;
506479 },
507480 .gzip => {
......@@ -520,37 +493,37 @@ pub const Reader = struct {
520493 }
521494
522495 fn contentLengthStream(
523 io_r: *std.io.Reader,
496 io_r: *std.Io.Reader,
524497 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));
528501 const remaining_content_length = &reader.state.body_remaining_content_length;
529502 const remaining = remaining_content_length.*;
530503 if (remaining == 0) {
531504 reader.state = .ready;
532505 return error.EndOfStream;
533506 }
534 const n = try reader.in.stream(w, limit.min(.limited(remaining)));
507 const n = try reader.in.stream(w, limit.min(.limited64(remaining)));
535508 remaining_content_length.* = remaining - n;
536509 return n;
537510 }
538511
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));
541514 const remaining_content_length = &reader.state.body_remaining_content_length;
542515 const remaining = remaining_content_length.*;
543516 if (remaining == 0) {
544517 reader.state = .ready;
545518 return error.EndOfStream;
546519 }
547 const n = try reader.in.discard(limit.min(.limited(remaining)));
520 const n = try reader.in.discard(limit.min(.limited64(remaining)));
548521 remaining_content_length.* = remaining - n;
549522 return n;
550523 }
551524
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));
554527 const chunk_len_ptr = switch (reader.state) {
555528 .ready => return error.EndOfStream,
556529 .body_remaining_chunk_len => |*x| x,
......@@ -573,9 +546,9 @@ pub const Reader = struct {
573546 fn chunkedReadEndless(
574547 reader: *Reader,
575548 w: *Writer,
576 limit: std.io.Limit,
549 limit: std.Io.Limit,
577550 chunk_len_ptr: *RemainingChunkLen,
578 ) (BodyError || std.io.Reader.StreamError)!usize {
551 ) (BodyError || std.Io.Reader.StreamError)!usize {
579552 const in = reader.in;
580553 len: switch (chunk_len_ptr.*) {
581554 .head => {
......@@ -596,7 +569,7 @@ pub const Reader = struct {
596569 }
597570 }
598571 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)));
600573 chunk_len_ptr.* = .init(cp.chunk_len + 2 - n);
601574 return n;
602575 },
......@@ -612,15 +585,15 @@ pub const Reader = struct {
612585 continue :len .head;
613586 },
614587 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)));
616589 chunk_len_ptr.* = .init(@intFromEnum(remaining_chunk_len) - n);
617590 return n;
618591 },
619592 }
620593 }
621594
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));
624597 const chunk_len_ptr = switch (reader.state) {
625598 .ready => return error.EndOfStream,
626599 .body_remaining_chunk_len => |*x| x,
......@@ -641,9 +614,9 @@ pub const Reader = struct {
641614
642615 fn chunkedDiscardEndless(
643616 reader: *Reader,
644 limit: std.io.Limit,
617 limit: std.Io.Limit,
645618 chunk_len_ptr: *RemainingChunkLen,
646 ) (BodyError || std.io.Reader.Error)!usize {
619 ) (BodyError || std.Io.Reader.Error)!usize {
647620 const in = reader.in;
648621 len: switch (chunk_len_ptr.*) {
649622 .head => {
......@@ -664,7 +637,7 @@ pub const Reader = struct {
664637 }
665638 }
666639 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)));
668641 chunk_len_ptr.* = .init(cp.chunk_len + 2 - n);
669642 return n;
670643 },
......@@ -680,7 +653,7 @@ pub const Reader = struct {
680653 continue :len .head;
681654 },
682655 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)));
684657 chunk_len_ptr.* = .init(remaining_chunk_len.int() - n);
685658 return n;
686659 },
......@@ -689,7 +662,7 @@ pub const Reader = struct {
689662
690663 /// Called when next bytes in the stream are trailers, or "\r\n" to indicate
691664 /// 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 {
693666 const in = reader.in;
694667 const rn = try in.peekArray(2);
695668 if (rn[0] == '\r' and rn[1] == '\n') {
......@@ -721,21 +694,21 @@ pub const Reader = struct {
721694pub const Decompressor = union(enum) {
722695 flate: std.compress.flate.Decompress,
723696 zstd: std.compress.zstd.Decompress,
724 none: *std.io.Reader,
697 none: *std.Io.Reader,
725698
726699 pub fn init(
727700 decompressor: *Decompressor,
728 transfer_reader: *std.io.Reader,
701 transfer_reader: *std.Io.Reader,
729702 buffer: []u8,
730703 content_encoding: ContentEncoding,
731 ) *std.io.Reader {
704 ) *std.Io.Reader {
732705 switch (content_encoding) {
733706 .identity => {
734707 decompressor.* = .{ .none = transfer_reader };
735708 return transfer_reader;
736709 },
737710 .deflate => {
738 decompressor.* = .{ .flate = .init(transfer_reader, .raw, buffer) };
711 decompressor.* = .{ .flate = .init(transfer_reader, .zlib, buffer) };
739712 return &decompressor.flate.reader;
740713 },
741714 .gzip => {
......@@ -763,7 +736,7 @@ pub const BodyWriter = struct {
763736
764737 /// How many zeroes to reserve for hex-encoded chunk length.
765738 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;
767740 const chunk_header_template = ("0" ** chunk_len_digits) ++ "\r\n";
768741
769742 comptime {
......@@ -795,7 +768,7 @@ pub const BodyWriter = struct {
795768 };
796769
797770 pub fn isEliding(w: *const BodyWriter) bool {
798 return w.writer.vtable.drain == Writer.discardingDrain;
771 return w.writer.vtable.drain == elidingDrain;
799772 }
800773
801774 /// Sends all buffered data across `BodyWriter.http_protocol_output`.
......@@ -923,7 +896,7 @@ pub const BodyWriter = struct {
923896 }
924897
925898 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));
927900 assert(!bw.isEliding());
928901 const out = bw.http_protocol_output;
929902 const n = try out.writeSplatHeader(w.buffered(), data, splat);
......@@ -932,24 +905,64 @@ pub const BodyWriter = struct {
932905 }
933906
934907 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));
936909 assert(!bw.isEliding());
937910 const out = bw.http_protocol_output;
938911 const n = try out.writeSplatHeader(w.buffered(), data, splat);
939912 return w.consume(n);
940913 }
941914
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
942955 /// 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));
945958 assert(!bw.isEliding());
946959 const out = bw.http_protocol_output;
947960 const n = try out.sendFileHeader(w.buffered(), file_reader, limit);
948961 return w.consume(n);
949962 }
950963
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));
953966 assert(!bw.isEliding());
954967 const out = bw.http_protocol_output;
955968 const n = try out.sendFileHeader(w.buffered(), file_reader, limit);
......@@ -957,8 +970,8 @@ pub const BodyWriter = struct {
957970 return w.consume(n);
958971 }
959972
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));
962975 assert(!bw.isEliding());
963976 const data_len = Writer.countSendFileLowerBound(w.end, file_reader, limit) orelse {
964977 // If the file size is unknown, we cannot lower to a `sendFile` since we would
......@@ -1006,7 +1019,7 @@ pub const BodyWriter = struct {
10061019 }
10071020
10081021 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));
10101023 assert(!bw.isEliding());
10111024 const out = bw.http_protocol_output;
10121025 const data_len = w.end + Writer.countSplat(data, splat);
lib/std/http/Client.zig+86-44
......@@ -42,7 +42,7 @@ connection_pool: ConnectionPool = .{},
4242///
4343/// If the entire HTTP header cannot fit in this amount of bytes,
4444/// `error.HttpHeadersOversize` will be returned from `Request.wait`.
45read_buffer_size: usize = 4096,
45read_buffer_size: usize = 4096 + if (disable_tls) 0 else std.crypto.tls.Client.min_buffer_len,
4646/// Each `Connection` allocates this amount for the writer buffer.
4747write_buffer_size: usize = 1024,
4848
......@@ -82,7 +82,7 @@ pub const ConnectionPool = struct {
8282
8383 var next = pool.free.last;
8484 while (next) |node| : (next = node.prev) {
85 const connection: *Connection = @fieldParentPtr("pool_node", node);
85 const connection: *Connection = @alignCast(@fieldParentPtr("pool_node", node));
8686 if (connection.protocol != criteria.protocol) continue;
8787 if (connection.port != criteria.port) continue;
8888
......@@ -115,8 +115,6 @@ pub const ConnectionPool = struct {
115115 /// Tries to release a connection back to the connection pool.
116116 /// If the connection is marked as closing, it will be closed instead.
117117 ///
118 /// `allocator` must be the same one used to create `connection`.
119 ///
120118 /// Threadsafe.
121119 pub fn release(pool: *ConnectionPool, connection: *Connection) void {
122120 pool.mutex.lock();
......@@ -127,7 +125,7 @@ pub const ConnectionPool = struct {
127125 if (connection.closing or pool.free_size == 0) return connection.destroy();
128126
129127 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().?));
131129 pool.free_len -= 1;
132130
133131 popped.destroy();
......@@ -183,14 +181,14 @@ pub const ConnectionPool = struct {
183181
184182 var next = pool.free.first;
185183 while (next) |node| {
186 const connection: *Connection = @fieldParentPtr("pool_node", node);
184 const connection: *Connection = @alignCast(@fieldParentPtr("pool_node", node));
187185 next = node.next;
188186 connection.destroy();
189187 }
190188
191189 next = pool.used.first;
192190 while (next) |node| {
193 const connection: *Connection = @fieldParentPtr("pool_node", node);
191 const connection: *Connection = @alignCast(@fieldParentPtr("pool_node", node));
194192 next = node.next;
195193 connection.destroy();
196194 }
......@@ -306,15 +304,16 @@ pub const Connection = struct {
306304 const host_buffer = base[@sizeOf(Tls)..][0..remote_host.len];
307305 const tls_read_buffer = host_buffer.ptr[host_buffer.len..][0..client.tls_buffer_size];
308306 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);
311310 @memcpy(host_buffer, remote_host);
312311 const tls: *Tls = @ptrCast(base);
313312 tls.* = .{
314313 .connection = .{
315314 .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),
318317 .pool_node = .{},
319318 .port = port,
320319 .host_len = @intCast(remote_host.len),
......@@ -330,8 +329,8 @@ pub const Connection = struct {
330329 .host = .{ .explicit = remote_host },
331330 .ca = .{ .bundle = client.ca_bundle },
332331 .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,
335334 // This is appropriate for HTTPS because the HTTP headers contain
336335 // the content length which is used to detect truncation attacks.
337336 .allow_truncation_attacks = true,
......@@ -349,7 +348,8 @@ pub const Connection = struct {
349348 }
350349
351350 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;
353353 }
354354
355355 fn host(tls: *Tls) []u8 {
......@@ -358,6 +358,21 @@ pub const Connection = struct {
358358 }
359359 };
360360
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
361376 fn getStream(c: *Connection) net.Stream {
362377 return c.stream_reader.getStream();
363378 }
......@@ -366,11 +381,11 @@ pub const Connection = struct {
366381 return switch (c.protocol) {
367382 .tls => {
368383 if (disable_tls) unreachable;
369 const tls: *Tls = @fieldParentPtr("connection", c);
384 const tls: *Tls = @alignCast(@fieldParentPtr("connection", c));
370385 return tls.host();
371386 },
372387 .plain => {
373 const plain: *Plain = @fieldParentPtr("connection", c);
388 const plain: *Plain = @alignCast(@fieldParentPtr("connection", c));
374389 return plain.host();
375390 },
376391 };
......@@ -383,11 +398,11 @@ pub const Connection = struct {
383398 switch (c.protocol) {
384399 .tls => {
385400 if (disable_tls) unreachable;
386 const tls: *Tls = @fieldParentPtr("connection", c);
401 const tls: *Tls = @alignCast(@fieldParentPtr("connection", c));
387402 tls.destroy();
388403 },
389404 .plain => {
390 const plain: *Plain = @fieldParentPtr("connection", c);
405 const plain: *Plain = @alignCast(@fieldParentPtr("connection", c));
391406 plain.destroy();
392407 },
393408 }
......@@ -399,7 +414,7 @@ pub const Connection = struct {
399414 return switch (c.protocol) {
400415 .tls => {
401416 if (disable_tls) unreachable;
402 const tls: *Tls = @fieldParentPtr("connection", c);
417 const tls: *Tls = @alignCast(@fieldParentPtr("connection", c));
403418 return &tls.client.writer;
404419 },
405420 .plain => &c.stream_writer.interface,
......@@ -412,7 +427,7 @@ pub const Connection = struct {
412427 return switch (c.protocol) {
413428 .tls => {
414429 if (disable_tls) unreachable;
415 const tls: *Tls = @fieldParentPtr("connection", c);
430 const tls: *Tls = @alignCast(@fieldParentPtr("connection", c));
416431 return &tls.client.reader;
417432 },
418433 .plain => c.stream_reader.interface(),
......@@ -422,7 +437,7 @@ pub const Connection = struct {
422437 pub fn flush(c: *Connection) Writer.Error!void {
423438 if (c.protocol == .tls) {
424439 if (disable_tls) unreachable;
425 const tls: *Tls = @fieldParentPtr("connection", c);
440 const tls: *Tls = @alignCast(@fieldParentPtr("connection", c));
426441 try tls.client.writer.flush();
427442 }
428443 try c.stream_writer.interface.flush();
......@@ -434,9 +449,8 @@ pub const Connection = struct {
434449 pub fn end(c: *Connection) Writer.Error!void {
435450 if (c.protocol == .tls) {
436451 if (disable_tls) unreachable;
437 const tls: *Tls = @fieldParentPtr("connection", c);
452 const tls: *Tls = @alignCast(@fieldParentPtr("connection", c));
438453 try tls.client.end();
439 try tls.client.writer.flush();
440454 }
441455 try c.stream_writer.interface.flush();
442456 }
......@@ -444,8 +458,8 @@ pub const Connection = struct {
444458
445459pub const Response = struct {
446460 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.
449463 head: Head,
450464
451465 pub const Head = struct {
......@@ -484,10 +498,8 @@ pub const Response = struct {
484498 };
485499 var it = mem.splitSequence(u8, bytes, "\r\n");
486500
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;
491503
492504 const version: http.Version = switch (int64(first_line[0..8])) {
493505 int64("HTTP/1.0") => .@"HTTP/1.0",
......@@ -671,6 +683,16 @@ pub const Response = struct {
671683 try expectEqual(@as(u10, 418), parseInt3("418"));
672684 try expectEqual(@as(u10, 999), parseInt3("999"));
673685 }
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 }
674696 };
675697
676698 /// If compressed body has been negotiated this will return compressed bytes.
......@@ -683,6 +705,7 @@ pub const Response = struct {
683705 /// See also:
684706 /// * `readerDecompressing`
685707 pub fn reader(response: *Response, buffer: []u8) *Reader {
708 response.head.invalidateStrings();
686709 const req = response.request;
687710 if (!req.method.responseHasBody()) return .ending;
688711 const head = &response.head;
......@@ -703,6 +726,7 @@ pub const Response = struct {
703726 decompressor: *http.Decompressor,
704727 decompression_buffer: []u8,
705728 ) *Reader {
729 response.head.invalidateStrings();
706730 const head = &response.head;
707731 return response.request.reader.bodyReaderDecompressing(
708732 head.transfer_encoding,
......@@ -805,6 +829,11 @@ pub const Request = struct {
805829 unhandled = std.math.maxInt(u16),
806830 _,
807831
832 pub fn init(n: u16) RedirectBehavior {
833 assert(n != std.math.maxInt(u16));
834 return @enumFromInt(n);
835 }
836
808837 pub fn subtractOne(rb: *RedirectBehavior) void {
809838 switch (rb.*) {
810839 .not_allowed => unreachable,
......@@ -821,7 +850,6 @@ pub const Request = struct {
821850
822851 /// Returns the request's `Connection` back to the pool of the `Client`.
823852 pub fn deinit(r: *Request) void {
824 r.reader.restituteHeadBuffer();
825853 if (r.connection) |connection| {
826854 connection.closing = connection.closing or switch (r.reader.state) {
827855 .ready => false,
......@@ -856,6 +884,15 @@ pub const Request = struct {
856884 return result;
857885 }
858886
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
859896 /// Transfers the HTTP head over the connection, which is not flushed until
860897 /// `BodyWriter.flush` or `BodyWriter.end` is called.
861898 ///
......@@ -908,13 +945,13 @@ pub const Request = struct {
908945 const connection = r.connection.?;
909946 const w = connection.writer();
910947
911 try r.method.write(w);
948 try w.writeAll(@tagName(r.method));
912949 try w.writeByte(' ');
913950
914951 if (r.method == .CONNECT) {
915 try uri.writeToStream(.{ .authority = true }, w);
952 try uri.writeToStream(w, .{ .authority = true });
916953 } else {
917 try uri.writeToStream(.{
954 try uri.writeToStream(w, .{
918955 .scheme = connection.proxied,
919956 .authentication = connection.proxied,
920957 .authority = connection.proxied,
......@@ -928,7 +965,7 @@ pub const Request = struct {
928965
929966 if (try emitOverridableHeader("host: ", r.headers.host, w)) {
930967 try w.writeAll("host: ");
931 try uri.writeToStream(.{ .authority = true }, w);
968 try uri.writeToStream(w, .{ .authority = true });
932969 try w.writeAll("\r\n");
933970 }
934971
......@@ -1043,13 +1080,16 @@ pub const Request = struct {
10431080 /// buffer capacity would be exceeded, `error.HttpRedirectLocationOversize`
10441081 /// is returned instead. This buffer may be empty if no redirects are to be
10451082 /// 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.
10461086 pub fn receiveHead(r: *Request, redirect_buffer: []u8) ReceiveHeadError!Response {
10471087 var aux_buf = redirect_buffer;
10481088 while (true) {
1049 try r.reader.receiveHead();
1089 const head_buffer = try r.reader.receiveHead();
10501090 const response: Response = .{
10511091 .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,
10531093 };
10541094 const head = &response.head;
10551095
......@@ -1121,7 +1161,6 @@ pub const Request = struct {
11211161 _ = reader.discardRemaining() catch |err| switch (err) {
11221162 error.ReadFailed => return r.reader.body_err.?,
11231163 };
1124 r.reader.restituteHeadBuffer();
11251164 }
11261165 const new_uri = r.uri.resolveInPlace(location.len, aux_buf) catch |err| switch (err) {
11271166 error.UnexpectedCharacter => return error.HttpRedirectLocationInvalid,
......@@ -1298,16 +1337,17 @@ pub const basic_authorization = struct {
12981337 pub fn value(uri: Uri, out: []u8) []u8 {
12991338 var bw: Writer = .fixed(out);
13001339 write(uri, &bw) catch unreachable;
1301 return bw.getWritten();
1340 return bw.buffered();
13021341 }
13031342
13041343 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;
13061345 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;
13111351 try out.print("Basic {b64}", .{w.buffered()});
13121352 }
13131353};
......@@ -1697,6 +1737,7 @@ pub const FetchError = Uri.ParseError || RequestError || Request.ReceiveHeadErro
16971737 StreamTooLong,
16981738 /// TODO provide optional diagnostics when this occurs or break into more error codes
16991739 WriteFailed,
1740 UnsupportedCompressionMethod,
17001741};
17011742
17021743/// Perform a one-shot HTTP request with the provided options.
......@@ -1748,7 +1789,8 @@ pub fn fetch(client: *Client, options: FetchOptions) FetchError!FetchResult {
17481789 const decompress_buffer: []u8 = switch (response.head.content_encoding) {
17491790 .identity => &.{},
17501791 .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,
17521794 };
17531795 defer if (options.decompress_buffer == null) client.allocator.free(decompress_buffer);
17541796
lib/std/http/Server.zig+64-47
......@@ -6,7 +6,8 @@ const mem = std.mem;
66const Uri = std.Uri;
77const assert = std.debug.assert;
88const testing = std.testing;
9const Writer = std.io.Writer;
9const Writer = std.Io.Writer;
10const Reader = std.Io.Reader;
1011
1112const Server = @This();
1213
......@@ -21,7 +22,7 @@ reader: http.Reader,
2122/// header, otherwise `receiveHead` returns `error.HttpHeadersOversize`.
2223///
2324/// The returned `Server` is ready for `receiveHead` to be called.
24pub fn init(in: *std.io.Reader, out: *Writer) Server {
25pub fn init(in: *Reader, out: *Writer) Server {
2526 return .{
2627 .reader = .{
2728 .in = in,
......@@ -33,33 +34,31 @@ pub fn init(in: *std.io.Reader, out: *Writer) Server {
3334 };
3435}
3536
36pub fn deinit(s: *Server) void {
37 s.reader.restituteHeadBuffer();
38}
39
4037pub const ReceiveHeadError = http.Reader.HeadError || error{
4138 /// Client sent headers that did not conform to the HTTP protocol.
4239 ///
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
4441 /// passed directly to `Request.Head.parse`.
4542 HttpHeadersInvalid,
4643};
4744
4845pub fn receiveHead(s: *Server) ReceiveHeadError!Request {
49 try s.reader.receiveHead();
46 const head_buffer = try s.reader.receiveHead();
5047 return .{
5148 .server = s,
49 .head_buffer = head_buffer,
5250 // No need to track the returned error here since users can repeat the
5351 // 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,
5553 };
5654}
5755
5856pub const Request = struct {
5957 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.
6260 head: Head,
61 head_buffer: []const u8,
6362 respond_err: ?RespondError = null,
6463
6564 pub const RespondError = error{
......@@ -98,10 +97,9 @@ pub const Request = struct {
9897
9998 const method_end = mem.indexOfScalar(u8, first_line, ' ') orelse
10099 return error.HttpHeadersInvalid;
101 if (method_end > 24) return error.HttpHeadersInvalid;
102100
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;
105103
106104 const version_start = mem.lastIndexOfScalar(u8, first_line, ' ') orelse
107105 return error.HttpHeadersInvalid;
......@@ -225,11 +223,19 @@ pub const Request = struct {
225223 inline fn int64(array: *const [8]u8) u64 {
226224 return @bitCast(array.*);
227225 }
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 }
228234 };
229235
230 pub fn iterateHeaders(r: *Request) http.HeaderIterator {
236 pub fn iterateHeaders(r: *const Request) http.HeaderIterator {
231237 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);
233239 }
234240
235241 test iterateHeaders {
......@@ -244,7 +250,6 @@ pub const Request = struct {
244250 .reader = .{
245251 .in = undefined,
246252 .state = .received_head,
247 .head_buffer = @constCast(request_bytes),
248253 .interface = undefined,
249254 },
250255 .out = undefined,
......@@ -253,6 +258,7 @@ pub const Request = struct {
253258 var request: Request = .{
254259 .server = &server,
255260 .head = undefined,
261 .head_buffer = @constCast(request_bytes),
256262 };
257263
258264 var it = request.iterateHeaders();
......@@ -435,10 +441,8 @@ pub const Request = struct {
435441
436442 for (o.extra_headers) |header| {
437443 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);
442446 }
443447
444448 try out.writeAll("\r\n");
......@@ -453,7 +457,13 @@ pub const Request = struct {
453457 return if (elide_body) .{
454458 .http_protocol_output = request.server.out,
455459 .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 },
457467 } else .{
458468 .http_protocol_output = request.server.out,
459469 .state = state,
......@@ -484,10 +494,11 @@ pub const Request = struct {
484494 none,
485495 };
486496
497 /// Does not invalidate `request.head`.
487498 pub fn upgradeRequested(request: *const Request) UpgradeRequest {
488499 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,
491502 }
492503
493504 var sec_websocket_key: ?[]const u8 = null;
......@@ -515,7 +526,7 @@ pub const Request = struct {
515526
516527 /// The header is not guaranteed to be sent until `WebSocket.flush` is
517528 /// 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 {
519530 if (request.head.expect != null) return error.HttpExpectationFailed;
520531
521532 const out = request.server.out;
......@@ -534,16 +545,14 @@ pub const Request = struct {
534545 try out.print("{s} {d} {s}\r\n", .{ @tagName(version), @intFromEnum(status), phrase });
535546 try out.writeAll("connection: upgrade\r\nupgrade: websocket\r\nsec-websocket-accept: ");
536547 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);
538549 out.advance(base64_digest.len);
539550 try out.writeAll("\r\n");
540551
541552 for (options.extra_headers) |header| {
542553 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);
547556 }
548557
549558 try out.writeAll("\r\n");
......@@ -564,7 +573,7 @@ pub const Request = struct {
564573 ///
565574 /// See `readerExpectNone` for an infallible alternative that cannot write
566575 /// 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 {
568577 const flush = request.head.expect != null;
569578 try writeExpectContinue(request);
570579 if (flush) try request.server.out.flush();
......@@ -576,9 +585,12 @@ pub const Request = struct {
576585 /// this function.
577586 ///
578587 /// 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 {
580591 assert(request.server.reader.state == .received_head);
581592 assert(request.head.expect == null);
593 request.head.invalidateStrings();
582594 if (!request.head.method.requestHasBody()) return .ending;
583595 return request.server.reader.bodyReader(buffer, request.head.transfer_encoding, request.head.content_length);
584596 }
......@@ -640,7 +652,7 @@ pub const Request = struct {
640652/// See https://tools.ietf.org/html/rfc6455
641653pub const WebSocket = struct {
642654 key: []const u8,
643 input: *std.io.Reader,
655 input: *Reader,
644656 output: *Writer,
645657
646658 pub const Header0 = packed struct(u8) {
......@@ -677,6 +689,8 @@ pub const WebSocket = struct {
677689 UnexpectedOpCode,
678690 MessageTooBig,
679691 MissingMaskBit,
692 ReadFailed,
693 EndOfStream,
680694 };
681695
682696 pub const SmallMessage = struct {
......@@ -691,8 +705,9 @@ pub const WebSocket = struct {
691705 pub fn readSmallMessage(ws: *WebSocket) ReadSmallTextMessageError!SmallMessage {
692706 const in = ws.input;
693707 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]);
696711
697712 switch (h0.opcode) {
698713 .text, .binary, .pong, .ping => {},
......@@ -732,47 +747,49 @@ pub const WebSocket = struct {
732747 }
733748
734749 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);
736752 try ws.output.flush();
737753 }
738754
739755 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);
741758 }
742759
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 {
744761 try writeMessageVecUnflushed(ws, data, op);
745762 try ws.output.flush();
746763 }
747764
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 {
749766 const total_len = l: {
750767 var total_len: u64 = 0;
751768 for (data) |iovec| total_len += iovec.len;
752769 break :l total_len;
753770 };
754771 const out = ws.output;
755 try out.writeStruct(@as(Header0, .{
772 try out.writeByte(@bitCast(@as(Header0, .{
756773 .opcode = op,
757774 .fin = true,
758 }));
775 })));
759776 switch (total_len) {
760 0...125 => try out.writeStruct(@as(Header1, .{
777 0...125 => try out.writeByte(@bitCast(@as(Header1, .{
761778 .payload_len = @enumFromInt(total_len),
762779 .mask = false,
763 })),
780 }))),
764781 126...0xffff => {
765 try out.writeStruct(@as(Header1, .{
782 try out.writeByte(@bitCast(@as(Header1, .{
766783 .payload_len = .len16,
767784 .mask = false,
768 }));
785 })));
769786 try out.writeInt(u16, @intCast(total_len), .big);
770787 },
771788 else => {
772 try out.writeStruct(@as(Header1, .{
789 try out.writeByte(@bitCast(@as(Header1, .{
773790 .payload_len = .len64,
774791 .mask = false,
775 }));
792 })));
776793 try out.writeInt(u64, total_len, .big);
777794 },
778795 }
lib/std/http/test.zig+70-58
......@@ -65,23 +65,22 @@ test "trailers" {
6565 try req.sendBodiless();
6666 var response = try req.receiveHead(&.{});
6767
68 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
69 defer gpa.free(body);
70
71 try expectEqualStrings("Hello, World!\n", body);
72
7368 {
7469 var it = response.head.iterateHeaders();
7570 const header = it.next().?;
76 try expect(!it.is_trailer);
7771 try expectEqualStrings("transfer-encoding", header.name);
7872 try expectEqualStrings("chunked", header.value);
7973 try expectEqual(null, it.next());
8074 }
75
76 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
77 defer gpa.free(body);
78
79 try expectEqualStrings("Hello, World!\n", body);
80
8181 {
8282 var it = response.iterateTrailers();
8383 const header = it.next().?;
84 try expect(it.is_trailer);
8584 try expectEqualStrings("X-Checksum", header.name);
8685 try expectEqualStrings("aaaa", header.value);
8786 try expectEqual(null, it.next());
......@@ -183,7 +182,11 @@ test "echo content server" {
183182 if (request.head.expect) |expect_header_value| {
184183 if (mem.eql(u8, expect_header_value, "garbage")) {
185184 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 });
187190 continue;
188191 }
189192 }
......@@ -204,12 +207,14 @@ test "echo content server" {
204207 // request.head.target,
205208 //});
206209
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);
208215 defer std.testing.allocator.free(body);
209216
210 try expect(mem.startsWith(u8, request.head.target, "/echo-content"));
211217 try expectEqualStrings("Hello, World!\n", body);
212 try expectEqualStrings("text/plain", request.head.content_type.?);
213218
214219 var response = try request.respondStreaming(&.{}, .{
215220 .content_length = switch (request.head.transfer_encoding) {
......@@ -273,7 +278,6 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
273278 for (0..500) |i| {
274279 try w.print("{d}, ah ha ha!\n", .{i});
275280 }
276 try expectEqual(7390, w.count);
277281 try w.flush();
278282 try response.end();
279283 try expectEqual(.closing, server.reader.state);
......@@ -291,7 +295,7 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
291295
292296 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded
293297 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);
295299 defer gpa.free(response);
296300
297301 var expected_response = std.ArrayList(u8).init(gpa);
......@@ -362,7 +366,7 @@ test "receiving arbitrary http headers from the client" {
362366
363367 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded
364368 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);
366370 defer gpa.free(response);
367371
368372 var expected_response = std.ArrayList(u8).init(gpa);
......@@ -407,18 +411,19 @@ test "general client/server API coverage" {
407411
408412 fn handleRequest(request: *http.Server.Request, listen_port: u16) !void {
409413 const log = std.log.scoped(.server);
414 const gpa = std.testing.allocator;
410415
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);
414419
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);
417422 defer gpa.free(body);
418423
419 if (mem.startsWith(u8, request.head.target, "/get")) {
424 if (mem.startsWith(u8, target, "/get")) {
420425 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)
422427 14
423428 else
424429 null,
......@@ -433,7 +438,7 @@ test "general client/server API coverage" {
433438 try w.writeAll("World!\n");
434439 try response.end();
435440 // 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")) {
437442 var response = try request.respondStreaming(&.{}, .{
438443 .content_length = 14 * 1024 + 14 * 10,
439444 });
......@@ -447,7 +452,8 @@ test "general client/server API coverage" {
447452 try w.writeAll("Hello, World!\n");
448453 }
449454
450 try w.writeAll("Hello, World!\n" ** 1024);
455 var vec: [1][]const u8 = .{"Hello, World!\n"};
456 try w.writeSplatAll(&vec, 1024);
451457
452458 i = 0;
453459 while (i < 5) : (i += 1) {
......@@ -455,7 +461,7 @@ test "general client/server API coverage" {
455461 }
456462
457463 try response.end();
458 } else if (mem.eql(u8, request.head.target, "/redirect/1")) {
464 } else if (mem.eql(u8, target, "/redirect/1")) {
459465 var response = try request.respondStreaming(&.{}, .{
460466 .respond_options = .{
461467 .status = .found,
......@@ -469,14 +475,14 @@ test "general client/server API coverage" {
469475 try w.writeAll("Hello, ");
470476 try w.writeAll("Redirected!\n");
471477 try response.end();
472 } else if (mem.eql(u8, request.head.target, "/redirect/2")) {
478 } else if (mem.eql(u8, target, "/redirect/2")) {
473479 try request.respond("Hello, Redirected!\n", .{
474480 .status = .found,
475481 .extra_headers = &.{
476482 .{ .name = "location", .value = "/redirect/1" },
477483 },
478484 });
479 } else if (mem.eql(u8, request.head.target, "/redirect/3")) {
485 } else if (mem.eql(u8, target, "/redirect/3")) {
480486 const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/redirect/2", .{
481487 listen_port,
482488 });
......@@ -488,23 +494,23 @@ test "general client/server API coverage" {
488494 .{ .name = "location", .value = location },
489495 },
490496 });
491 } else if (mem.eql(u8, request.head.target, "/redirect/4")) {
497 } else if (mem.eql(u8, target, "/redirect/4")) {
492498 try request.respond("Hello, Redirected!\n", .{
493499 .status = .found,
494500 .extra_headers = &.{
495501 .{ .name = "location", .value = "/redirect/3" },
496502 },
497503 });
498 } else if (mem.eql(u8, request.head.target, "/redirect/5")) {
504 } else if (mem.eql(u8, target, "/redirect/5")) {
499505 try request.respond("Hello, Redirected!\n", .{
500506 .status = .found,
501507 .extra_headers = &.{
502508 .{ .name = "location", .value = "/%2525" },
503509 },
504510 });
505 } else if (mem.eql(u8, request.head.target, "/%2525")) {
511 } else if (mem.eql(u8, target, "/%2525")) {
506512 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")) {
508514 const invalid_port = try getUnusedTcpPort();
509515 const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}", .{invalid_port});
510516 defer gpa.free(location);
......@@ -515,7 +521,7 @@ test "general client/server API coverage" {
515521 .{ .name = "location", .value = location },
516522 },
517523 });
518 } else if (mem.eql(u8, request.head.target, "/empty")) {
524 } else if (mem.eql(u8, target, "/empty")) {
519525 try request.respond("", .{
520526 .extra_headers = &.{
521527 .{ .name = "empty", .value = "" },
......@@ -556,11 +562,12 @@ test "general client/server API coverage" {
556562 try req.sendBodiless();
557563 var response = try req.receiveHead(&redirect_buffer);
558564
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);
560568 defer gpa.free(body);
561569
562570 try expectEqualStrings("Hello, World!\n", body);
563 try expectEqualStrings("text/plain", response.head.content_type.?);
564571 }
565572
566573 // connection has been kept alive
......@@ -579,7 +586,7 @@ test "general client/server API coverage" {
579586 try req.sendBodiless();
580587 var response = try req.receiveHead(&redirect_buffer);
581588
582 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192 * 1024));
589 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
583590 defer gpa.free(body);
584591
585592 try expectEqual(@as(usize, 14 * 1024 + 14 * 10), body.len);
......@@ -601,12 +608,13 @@ test "general client/server API coverage" {
601608 try req.sendBodiless();
602609 var response = try req.receiveHead(&redirect_buffer);
603610
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);
605615 defer gpa.free(body);
606616
607617 try expectEqualStrings("", body);
608 try expectEqualStrings("text/plain", response.head.content_type.?);
609 try expectEqual(14, response.head.content_length.?);
610618 }
611619
612620 // connection has been kept alive
......@@ -625,11 +633,12 @@ test "general client/server API coverage" {
625633 try req.sendBodiless();
626634 var response = try req.receiveHead(&redirect_buffer);
627635
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);
629639 defer gpa.free(body);
630640
631641 try expectEqualStrings("Hello, World!\n", body);
632 try expectEqualStrings("text/plain", response.head.content_type.?);
633642 }
634643
635644 // connection has been kept alive
......@@ -648,12 +657,13 @@ test "general client/server API coverage" {
648657 try req.sendBodiless();
649658 var response = try req.receiveHead(&redirect_buffer);
650659
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);
652664 defer gpa.free(body);
653665
654666 try expectEqualStrings("", body);
655 try expectEqualStrings("text/plain", response.head.content_type.?);
656 try expect(response.head.transfer_encoding == .chunked);
657667 }
658668
659669 // connection has been kept alive
......@@ -674,11 +684,12 @@ test "general client/server API coverage" {
674684 try req.sendBodiless();
675685 var response = try req.receiveHead(&redirect_buffer);
676686
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);
678690 defer gpa.free(body);
679691
680692 try expectEqualStrings("Hello, World!\n", body);
681 try expectEqualStrings("text/plain", response.head.content_type.?);
682693 }
683694
684695 // connection has been closed
......@@ -703,11 +714,6 @@ test "general client/server API coverage" {
703714
704715 try std.testing.expectEqual(.ok, response.head.status);
705716
706 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
707 defer gpa.free(body);
708
709 try expectEqualStrings("", body);
710
711717 var it = response.head.iterateHeaders();
712718 {
713719 const header = it.next().?;
......@@ -715,6 +721,12 @@ test "general client/server API coverage" {
715721 try expectEqualStrings("content-length", header.name);
716722 try expectEqualStrings("0", header.value);
717723 }
724
725 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
726 defer gpa.free(body);
727
728 try expectEqualStrings("", body);
729
718730 {
719731 const header = it.next().?;
720732 try expect(!it.is_trailer);
......@@ -740,7 +752,7 @@ test "general client/server API coverage" {
740752 try req.sendBodiless();
741753 var response = try req.receiveHead(&redirect_buffer);
742754
743 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
755 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
744756 defer gpa.free(body);
745757
746758 try expectEqualStrings("Hello, World!\n", body);
......@@ -762,7 +774,7 @@ test "general client/server API coverage" {
762774 try req.sendBodiless();
763775 var response = try req.receiveHead(&redirect_buffer);
764776
765 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
777 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
766778 defer gpa.free(body);
767779
768780 try expectEqualStrings("Hello, World!\n", body);
......@@ -784,7 +796,7 @@ test "general client/server API coverage" {
784796 try req.sendBodiless();
785797 var response = try req.receiveHead(&redirect_buffer);
786798
787 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
799 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
788800 defer gpa.free(body);
789801
790802 try expectEqualStrings("Hello, World!\n", body);
......@@ -825,7 +837,7 @@ test "general client/server API coverage" {
825837 try req.sendBodiless();
826838 var response = try req.receiveHead(&redirect_buffer);
827839
828 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
840 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
829841 defer gpa.free(body);
830842
831843 try expectEqualStrings("Encoded redirect successful!\n", body);
......@@ -915,7 +927,7 @@ test "Server streams both reading and writing" {
915927 try body_writer.writer.writeAll("fish");
916928 try body_writer.end();
917929
918 const body = try response.reader(&.{}).allocRemaining(std.testing.allocator, .limited(8192));
930 const body = try response.reader(&.{}).allocRemaining(std.testing.allocator, .unlimited);
919931 defer std.testing.allocator.free(body);
920932
921933 try expectEqualStrings("ONE FISH", body);
......@@ -947,7 +959,7 @@ fn echoTests(client: *http.Client, port: u16) !void {
947959
948960 var response = try req.receiveHead(&redirect_buffer);
949961
950 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
962 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
951963 defer gpa.free(body);
952964
953965 try expectEqualStrings("Hello, World!\n", body);
......@@ -980,7 +992,7 @@ fn echoTests(client: *http.Client, port: u16) !void {
980992
981993 var response = try req.receiveHead(&redirect_buffer);
982994
983 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
995 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
984996 defer gpa.free(body);
985997
986998 try expectEqualStrings("Hello, World!\n", body);
......@@ -1034,7 +1046,7 @@ fn echoTests(client: *http.Client, port: u16) !void {
10341046 var response = try req.receiveHead(&redirect_buffer);
10351047 try expectEqual(.ok, response.head.status);
10361048
1037 const body = try response.reader(&.{}).allocRemaining(gpa, .limited(8192));
1049 const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited);
10381050 defer gpa.free(body);
10391051
10401052 try expectEqualStrings("Hello, World!\n", body);
......@@ -1175,7 +1187,7 @@ test "redirect to different connection" {
11751187 var response = try req.receiveHead(&redirect_buffer);
11761188 var reader = response.reader(&.{});
11771189
1178 const body = try reader.allocRemaining(gpa, .limited(8192));
1190 const body = try reader.allocRemaining(gpa, .unlimited);
11791191 defer gpa.free(body);
11801192
11811193 try expectEqualStrings("good job, you pass", body);
lib/std/math/big/int.zig+3-5
......@@ -1710,7 +1710,7 @@ pub const Mutable = struct {
17101710
17111711 if (xy_trailing != 0 and r.limbs[r.len - 1] != 0) {
17121712 // 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]);
17141714 @memset(r.limbs[0..xy_trailing], 0);
17151715 r.len += xy_trailing;
17161716 }
......@@ -3836,8 +3836,7 @@ fn llshl(r: []Limb, a: []const Limb, shift: usize) usize {
38363836 std.debug.assert(@intFromPtr(r.ptr) >= @intFromPtr(a.ptr));
38373837
38383838 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);
38413840 return a.len;
38423841 }
38433842 if (shift >= limb_bits) {
......@@ -3891,8 +3890,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) usize {
38913890 if (shift == 0) {
38923891 std.debug.assert(r.len >= a.len);
38933892
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);
38963894 return a.len;
38973895 }
38983896 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 {
939939 .type = T,
940940 .default_value_ptr = null,
941941 .is_comptime = false,
942 .alignment = 0,
942 .alignment = @alignOf(T),
943943 };
944944 }
945945
lib/std/net.zig+1-1
......@@ -1944,7 +1944,7 @@ pub const Stream = struct {
19441944 pub const Error = ReadError;
19451945
19461946 pub fn getStream(r: *const Reader) Stream {
1947 return r.stream;
1947 return r.net_stream;
19481948 }
19491949
19501950 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 {
10141014 return syscall2(.munmap, @intFromPtr(address), length);
10151015}
10161016
1017pub fn mlock(address: [*]const u8, length: usize) usize {
1018 return syscall2(.mlock, @intFromPtr(address), length);
1019}
1020
1021pub fn munlock(address: [*]const u8, length: usize) usize {
1022 return syscall2(.munlock, @intFromPtr(address), length);
1023}
1024
1025pub const MLOCK = packed struct(u32) {
1026 ONFAULT: bool = false,
1027 _1: u31 = 0,
1028};
1029
1030pub fn mlock2(address: [*]const u8, length: usize, flags: MLOCK) usize {
1031 return syscall3(.mlock2, @intFromPtr(address), length, @as(u32, @bitCast(flags)));
1032}
1033
1034pub 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
1047pub fn mlockall(flags: MCL) usize {
1048 return syscall1(.mlockall, @as(u32, @bitCast(flags)));
1049}
1050
1051pub fn munlockall() usize {
1052 return syscall0(.munlockall);
1053}
1054
10171055pub fn poll(fds: [*]pollfd, n: nfds_t, timeout: i32) usize {
10181056 if (@hasField(SYS, "poll")) {
10191057 return syscall3(.poll, @intFromPtr(fds), n, @as(u32, @bitCast(timeout)));
lib/std/os/windows.zig+3-8
......@@ -1332,7 +1332,7 @@ pub fn GetFinalPathNameByHandle(
13321332 // dropping the \Device\Mup\ and making sure the path begins with \\
13331333 if (mem.eql(u16, device_name_u16, std.unicode.utf8ToUtf16LeStringLiteral("Mup"))) {
13341334 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);
13361336 return out_buffer[0 .. 1 + file_name_u16.len];
13371337 }
13381338
......@@ -1400,7 +1400,7 @@ pub fn GetFinalPathNameByHandle(
14001400 if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
14011401
14021402 @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);
14041404 const total_len = drive_letter.len + file_name_u16.len;
14051405
14061406 // Validate that DOS does not contain any spurious nul bytes.
......@@ -1449,12 +1449,7 @@ pub fn GetFinalPathNameByHandle(
14491449 // to copy backwards. We also need to do this before copying the volume path because
14501450 // it could overwrite the file_name_u16 memory.
14511451 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);
14581453 @memcpy(out_buffer[0..volume_path.len], volume_path);
14591454 const total_len = volume_path.len + file_name_u16.len;
14601455
lib/std/process/Child.zig-9
......@@ -901,11 +901,6 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
901901 if (dir_buf.items.len > 0) try dir_buf.append(self.allocator, fs.path.sep);
902902 try dir_buf.appendSlice(self.allocator, app_dir);
903903 }
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 }
909904
910905 windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo) catch |no_path_err| {
911906 const original_err = switch (no_path_err) {
......@@ -930,10 +925,6 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
930925 while (it.next()) |search_path| {
931926 dir_buf.clearRetainingCapacity();
932927 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);
937928
938929 if (windowsCreateProcessPathExt(self.allocator, &dir_buf, &app_buf, PATHEXT, &cmd_line_cache, envp_ptr, cwd_w_ptr, flags, &siStartInfo, &piProcInfo)) {
939930 break :run;
lib/std/std.zig-2
......@@ -9,8 +9,6 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
99pub const AutoHashMap = hash_map.AutoHashMap;
1010pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
1111pub const BitStack = @import("BitStack.zig");
12pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
13pub const BoundedArrayAligned = @import("bounded_array.zig").BoundedArrayAligned;
1412pub const Build = @import("Build.zig");
1513pub const BufMap = @import("buf_map.zig").BufMap;
1614pub const BufSet = @import("buf_set.zig").BufSet;
lib/std/zig/AstGen.zig+3
......@@ -5386,6 +5386,9 @@ fn unionDeclInner(
53865386 return astgen.failNode(member_node, "union field missing type", .{});
53875387 }
53885388 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 }
53895392 const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, align_expr);
53905393 wip_members.appendToField(@intFromEnum(align_inst));
53915394 any_aligned_fields = true;
lib/std/zig/llvm/Builder.zig+4-3
......@@ -8533,18 +8533,19 @@ pub const Metadata = enum(u32) {
85338533 .type = []const u8,
85348534 .default_value_ptr = null,
85358535 .is_comptime = false,
8536 .alignment = 0,
8536 .alignment = @alignOf([]const u8),
85378537 };
85388538 }
85398539 fmt_str = fmt_str ++ "(";
85408540 inline for (fields[2..], names) |*field, name| {
85418541 fmt_str = fmt_str ++ "{[" ++ name ++ "]f}";
8542 const T = std.fmt.Formatter(FormatData, format);
85428543 field.* = .{
85438544 .name = name,
8544 .type = std.fmt.Formatter(FormatData, format),
8545 .type = T,
85458546 .default_value_ptr = null,
85468547 .is_comptime = false,
8547 .alignment = 0,
8548 .alignment = @alignOf(T),
85488549 };
85498550 }
85508551 fmt_str = fmt_str ++ ")\n";
src/Compilation.zig+86-52
......@@ -2103,6 +2103,8 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
21032103 .local_zir_cache = local_zir_cache,
21042104 .error_limit = error_limit,
21052105 .llvm_object = null,
2106 .analysis_roots_buffer = undefined,
2107 .analysis_roots_len = 0,
21062108 };
21072109 try zcu.init(options.thread_pool.getIdCount());
21082110 break :blk zcu;
......@@ -2183,8 +2185,12 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
21832185 .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
21842186 };
21852187
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), {});
21882194
21892195 // Prevent some footguns by making the "any" fields of config reflect
21902196 // the default Module settings.
......@@ -2378,7 +2384,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
23782384 };
23792385 comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
23802386 }
2381 comp.link_task_queue.pending_prelink_tasks += @intCast(comp.c_object_table.count());
23822387
23832388 // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.
23842389 const win32_resource_count =
......@@ -2386,10 +2391,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
23862391 if (win32_resource_count > 0) {
23872392 dev.check(.win32_resource);
23882393 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);
23932394 for (options.rc_source_files) |rc_source_file| {
23942395 const win32_resource = try gpa.create(Win32Resource);
23952396 errdefer gpa.destroy(win32_resource);
......@@ -2415,13 +2416,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
24152416
24162417 if (comp.emit_bin != null and target.ofmt != .c) {
24172418 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
24252419 // If we need to build libc for the target, add work items for it.
24262420 // We go through the work queue so that building can be done in parallel.
24272421 // If linking against host libc installation, instead queue up jobs
......@@ -2455,62 +2449,51 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
24552449
24562450 if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
24572451 comp.queued_jobs.musl_crt_file[@intFromEnum(f)] = true;
2458 comp.link_task_queue.pending_prelink_tasks += 1;
24592452 }
24602453 switch (comp.config.link_mode) {
24612454 .static => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_a)] = true,
24622455 .dynamic => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_so)] = true,
24632456 }
2464 comp.link_task_queue.pending_prelink_tasks += 1;
24652457 } else if (target.isGnuLibC()) {
24662458 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
24672459
24682460 if (glibc.needsCrt0(comp.config.output_mode)) |f| {
24692461 comp.queued_jobs.glibc_crt_file[@intFromEnum(f)] = true;
2470 comp.link_task_queue.pending_prelink_tasks += 1;
24712462 }
24722463 comp.queued_jobs.glibc_shared_objects = true;
2473 comp.link_task_queue.pending_prelink_tasks += glibc.sharedObjectsCount(target);
24742464
24752465 comp.queued_jobs.glibc_crt_file[@intFromEnum(glibc.CrtFile.libc_nonshared_a)] = true;
2476 comp.link_task_queue.pending_prelink_tasks += 1;
24772466 } else if (target.isFreeBSDLibC()) {
24782467 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
24792468
24802469 if (freebsd.needsCrt0(comp.config.output_mode)) |f| {
24812470 comp.queued_jobs.freebsd_crt_file[@intFromEnum(f)] = true;
2482 comp.link_task_queue.pending_prelink_tasks += 1;
24832471 }
24842472
24852473 comp.queued_jobs.freebsd_shared_objects = true;
2486 comp.link_task_queue.pending_prelink_tasks += freebsd.sharedObjectsCount();
24872474 } else if (target.isNetBSDLibC()) {
24882475 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
24892476
24902477 if (netbsd.needsCrt0(comp.config.output_mode)) |f| {
24912478 comp.queued_jobs.netbsd_crt_file[@intFromEnum(f)] = true;
2492 comp.link_task_queue.pending_prelink_tasks += 1;
24932479 }
24942480
24952481 comp.queued_jobs.netbsd_shared_objects = true;
2496 comp.link_task_queue.pending_prelink_tasks += netbsd.sharedObjectsCount();
24972482 } else if (target.isWasiLibC()) {
24982483 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
24992484
25002485 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true;
25012486 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true;
2502 comp.link_task_queue.pending_prelink_tasks += 2;
25032487 } else if (target.isMinGW()) {
25042488 if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
25052489
25062490 const main_crt_file: mingw.CrtFile = if (is_dyn_lib) .dllcrt2_o else .crt2_o;
25072491 comp.queued_jobs.mingw_crt_file[@intFromEnum(main_crt_file)] = true;
25082492 comp.queued_jobs.mingw_crt_file[@intFromEnum(mingw.CrtFile.libmingw32_lib)] = true;
2509 comp.link_task_queue.pending_prelink_tasks += 2;
25102493
25112494 // When linking mingw-w64 there are some import libs we always need.
25122495 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), {});
25142497 } else {
25152498 return error.LibCUnavailable;
25162499 }
......@@ -2520,7 +2503,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
25202503 target.isMinGW())
25212504 {
25222505 comp.queued_jobs.zigc_lib = true;
2523 comp.link_task_queue.pending_prelink_tasks += 1;
25242506 }
25252507 }
25262508
......@@ -2537,50 +2519,41 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
25372519 }
25382520 if (comp.wantBuildLibUnwindFromSource()) {
25392521 comp.queued_jobs.libunwind = true;
2540 comp.link_task_queue.pending_prelink_tasks += 1;
25412522 }
25422523 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
25432524 comp.queued_jobs.libcxx = true;
25442525 comp.queued_jobs.libcxxabi = true;
2545 comp.link_task_queue.pending_prelink_tasks += 2;
25462526 }
25472527 if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
25482528 comp.queued_jobs.libtsan = true;
2549 comp.link_task_queue.pending_prelink_tasks += 1;
25502529 }
25512530
25522531 if (can_build_compiler_rt) {
25532532 if (comp.compiler_rt_strat == .lib) {
25542533 log.debug("queuing a job to build compiler_rt_lib", .{});
25552534 comp.queued_jobs.compiler_rt_lib = true;
2556 comp.link_task_queue.pending_prelink_tasks += 1;
25572535 } else if (comp.compiler_rt_strat == .obj) {
25582536 log.debug("queuing a job to build compiler_rt_obj", .{});
25592537 // In this case we are making a static library, so we ask
25602538 // for a compiler-rt object to put in it.
25612539 comp.queued_jobs.compiler_rt_obj = true;
2562 comp.link_task_queue.pending_prelink_tasks += 1;
25632540 } else if (comp.compiler_rt_strat == .dyn_lib) {
25642541 // hack for stage2_x86_64 + coff
25652542 log.debug("queuing a job to build compiler_rt_dyn_lib", .{});
25662543 comp.queued_jobs.compiler_rt_dyn_lib = true;
2567 comp.link_task_queue.pending_prelink_tasks += 1;
25682544 }
25692545
25702546 if (comp.ubsan_rt_strat == .lib) {
25712547 log.debug("queuing a job to build ubsan_rt_lib", .{});
25722548 comp.queued_jobs.ubsan_rt_lib = true;
2573 comp.link_task_queue.pending_prelink_tasks += 1;
25742549 } else if (comp.ubsan_rt_strat == .obj) {
25752550 log.debug("queuing a job to build ubsan_rt_obj", .{});
25762551 comp.queued_jobs.ubsan_rt_obj = true;
2577 comp.link_task_queue.pending_prelink_tasks += 1;
25782552 }
25792553
25802554 if (is_exe_or_dyn_lib and comp.config.any_fuzz) {
25812555 log.debug("queuing a job to build libfuzzer", .{});
25822556 comp.queued_jobs.fuzzer_lib = true;
2583 comp.link_task_queue.pending_prelink_tasks += 1;
25842557 }
25852558 }
25862559 }
......@@ -2588,8 +2561,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
25882561 try comp.link_task_queue.queued_prelink.append(gpa, .load_explicitly_provided);
25892562 }
25902563 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
25932564 return comp;
25942565}
25952566
......@@ -2608,6 +2579,7 @@ pub fn destroy(comp: *Compilation) void {
26082579 comp.c_object_work_queue.deinit();
26092580 comp.win32_resource_work_queue.deinit();
26102581
2582 for (comp.windows_libs.keys()) |windows_lib| gpa.free(windows_lib);
26112583 comp.windows_libs.deinit(gpa);
26122584
26132585 {
......@@ -2933,22 +2905,26 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
29332905 try comp.appendFileSystemInput(embed_file.path);
29342906 }
29352907
2936 zcu.analysis_roots.clear();
2908 zcu.analysis_roots_len = 0;
29372909
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;
29392912
29402913 // Normally we rely on importing std to in turn import the root source file in the start code.
29412914 // However, the main module is distinct from the root module in tests, so that won't happen there.
29422915 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;
29442918 }
29452919
29462920 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;
29482923 }
29492924
29502925 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;
29522928 }
29532929 }
29542930
......@@ -4404,10 +4380,8 @@ fn performAllTheWork(
44044380 comp.link_task_wait_group.reset();
44054381 defer comp.link_task_wait_group.wait();
44064382
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);
44114385 comp.link_task_queue.start(comp);
44124386
44134387 if (comp.emit_docs != null) {
......@@ -4423,6 +4397,7 @@ fn performAllTheWork(
44234397 // compiler-rt due to LLD bugs as well, e.g.:
44244398 //
44254399 // https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611
4400 comp.link_task_queue.startPrelinkItem();
44264401 comp.link_task_wait_group.spawnManager(buildRt, .{
44274402 comp,
44284403 "compiler_rt.zig",
......@@ -4440,6 +4415,7 @@ fn performAllTheWork(
44404415 }
44414416
44424417 if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {
4418 comp.link_task_queue.startPrelinkItem();
44434419 comp.link_task_wait_group.spawnManager(buildRt, .{
44444420 comp,
44454421 "compiler_rt.zig",
......@@ -4458,6 +4434,7 @@ fn performAllTheWork(
44584434
44594435 // hack for stage2_x86_64 + coff
44604436 if (comp.queued_jobs.compiler_rt_dyn_lib and comp.compiler_rt_dyn_lib == null) {
4437 comp.link_task_queue.startPrelinkItem();
44614438 comp.link_task_wait_group.spawnManager(buildRt, .{
44624439 comp,
44634440 "compiler_rt.zig",
......@@ -4475,6 +4452,7 @@ fn performAllTheWork(
44754452 }
44764453
44774454 if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {
4455 comp.link_task_queue.startPrelinkItem();
44784456 comp.link_task_wait_group.spawnManager(buildRt, .{
44794457 comp,
44804458 "fuzzer.zig",
......@@ -4489,6 +4467,7 @@ fn performAllTheWork(
44894467 }
44904468
44914469 if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
4470 comp.link_task_queue.startPrelinkItem();
44924471 comp.link_task_wait_group.spawnManager(buildRt, .{
44934472 comp,
44944473 "ubsan_rt.zig",
......@@ -4505,6 +4484,7 @@ fn performAllTheWork(
45054484 }
45064485
45074486 if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
4487 comp.link_task_queue.startPrelinkItem();
45084488 comp.link_task_wait_group.spawnManager(buildRt, .{
45094489 comp,
45104490 "ubsan_rt.zig",
......@@ -4521,40 +4501,49 @@ fn performAllTheWork(
45214501 }
45224502
45234503 if (comp.queued_jobs.glibc_shared_objects) {
4504 comp.link_task_queue.startPrelinkItem();
45244505 comp.link_task_wait_group.spawnManager(buildGlibcSharedObjects, .{ comp, main_progress_node });
45254506 }
45264507
45274508 if (comp.queued_jobs.freebsd_shared_objects) {
4509 comp.link_task_queue.startPrelinkItem();
45284510 comp.link_task_wait_group.spawnManager(buildFreeBSDSharedObjects, .{ comp, main_progress_node });
45294511 }
45304512
45314513 if (comp.queued_jobs.netbsd_shared_objects) {
4514 comp.link_task_queue.startPrelinkItem();
45324515 comp.link_task_wait_group.spawnManager(buildNetBSDSharedObjects, .{ comp, main_progress_node });
45334516 }
45344517
45354518 if (comp.queued_jobs.libunwind) {
4519 comp.link_task_queue.startPrelinkItem();
45364520 comp.link_task_wait_group.spawnManager(buildLibUnwind, .{ comp, main_progress_node });
45374521 }
45384522
45394523 if (comp.queued_jobs.libcxx) {
4524 comp.link_task_queue.startPrelinkItem();
45404525 comp.link_task_wait_group.spawnManager(buildLibCxx, .{ comp, main_progress_node });
45414526 }
45424527
45434528 if (comp.queued_jobs.libcxxabi) {
4529 comp.link_task_queue.startPrelinkItem();
45444530 comp.link_task_wait_group.spawnManager(buildLibCxxAbi, .{ comp, main_progress_node });
45454531 }
45464532
45474533 if (comp.queued_jobs.libtsan) {
4534 comp.link_task_queue.startPrelinkItem();
45484535 comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node });
45494536 }
45504537
45514538 if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) {
4539 comp.link_task_queue.startPrelinkItem();
45524540 comp.link_task_wait_group.spawnManager(buildLibZigC, .{ comp, main_progress_node });
45534541 }
45544542
45554543 for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| {
45564544 if (comp.queued_jobs.musl_crt_file[i]) {
45574545 const tag: musl.CrtFile = @enumFromInt(i);
4546 comp.link_task_queue.startPrelinkItem();
45584547 comp.link_task_wait_group.spawnManager(buildMuslCrtFile, .{ comp, tag, main_progress_node });
45594548 }
45604549 }
......@@ -4562,6 +4551,7 @@ fn performAllTheWork(
45624551 for (0..@typeInfo(glibc.CrtFile).@"enum".fields.len) |i| {
45634552 if (comp.queued_jobs.glibc_crt_file[i]) {
45644553 const tag: glibc.CrtFile = @enumFromInt(i);
4554 comp.link_task_queue.startPrelinkItem();
45654555 comp.link_task_wait_group.spawnManager(buildGlibcCrtFile, .{ comp, tag, main_progress_node });
45664556 }
45674557 }
......@@ -4569,6 +4559,7 @@ fn performAllTheWork(
45694559 for (0..@typeInfo(freebsd.CrtFile).@"enum".fields.len) |i| {
45704560 if (comp.queued_jobs.freebsd_crt_file[i]) {
45714561 const tag: freebsd.CrtFile = @enumFromInt(i);
4562 comp.link_task_queue.startPrelinkItem();
45724563 comp.link_task_wait_group.spawnManager(buildFreeBSDCrtFile, .{ comp, tag, main_progress_node });
45734564 }
45744565 }
......@@ -4576,6 +4567,7 @@ fn performAllTheWork(
45764567 for (0..@typeInfo(netbsd.CrtFile).@"enum".fields.len) |i| {
45774568 if (comp.queued_jobs.netbsd_crt_file[i]) {
45784569 const tag: netbsd.CrtFile = @enumFromInt(i);
4570 comp.link_task_queue.startPrelinkItem();
45794571 comp.link_task_wait_group.spawnManager(buildNetBSDCrtFile, .{ comp, tag, main_progress_node });
45804572 }
45814573 }
......@@ -4583,6 +4575,7 @@ fn performAllTheWork(
45834575 for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {
45844576 if (comp.queued_jobs.wasi_libc_crt_file[i]) {
45854577 const tag: wasi_libc.CrtFile = @enumFromInt(i);
4578 comp.link_task_queue.startPrelinkItem();
45864579 comp.link_task_wait_group.spawnManager(buildWasiLibcCrtFile, .{ comp, tag, main_progress_node });
45874580 }
45884581 }
......@@ -4590,6 +4583,7 @@ fn performAllTheWork(
45904583 for (0..@typeInfo(mingw.CrtFile).@"enum".fields.len) |i| {
45914584 if (comp.queued_jobs.mingw_crt_file[i]) {
45924585 const tag: mingw.CrtFile = @enumFromInt(i);
4586 comp.link_task_queue.startPrelinkItem();
45934587 comp.link_task_wait_group.spawnManager(buildMingwCrtFile, .{ comp, tag, main_progress_node });
45944588 }
45954589 }
......@@ -4661,12 +4655,14 @@ fn performAllTheWork(
46614655 }
46624656
46634657 while (comp.c_object_work_queue.readItem()) |c_object| {
4658 comp.link_task_queue.startPrelinkItem();
46644659 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateCObject, .{
46654660 comp, c_object, main_progress_node,
46664661 });
46674662 }
46684663
46694664 while (comp.win32_resource_work_queue.readItem()) |win32_resource| {
4665 comp.link_task_queue.startPrelinkItem();
46704666 comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateWin32Resource, .{
46714667 comp, win32_resource, main_progress_node,
46724668 });
......@@ -4745,7 +4741,7 @@ fn performAllTheWork(
47454741 try zcu.flushRetryableFailures();
47464742
47474743 // It's analysis time! Queue up our initial analysis.
4748 for (zcu.analysis_roots.slice()) |mod| {
4744 for (zcu.analysisRoots()) |mod| {
47494745 try comp.queueJob(.{ .analyze_mod = mod });
47504746 }
47514747
......@@ -4769,15 +4765,14 @@ fn performAllTheWork(
47694765 }
47704766 };
47714767
4768 // We aren't going to queue any more prelink tasks.
4769 comp.link_task_queue.finishPrelinkItem(comp);
4770
47724771 if (!comp.separateCodegenThreadOk()) {
47734772 // Waits until all input files have been parsed.
47744773 comp.link_task_wait_group.wait();
47754774 comp.link_task_wait_group.reset();
47764775 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 }
47814776 }
47824777
47834778 if (comp.zcu != null) {
......@@ -5564,6 +5559,7 @@ fn workerUpdateCObject(
55645559 c_object: *CObject,
55655560 progress_node: std.Progress.Node,
55665561) void {
5562 defer comp.link_task_queue.finishPrelinkItem(comp);
55675563 comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
55685564 error.AnalysisFail => return,
55695565 else => {
......@@ -5581,6 +5577,7 @@ fn workerUpdateWin32Resource(
55815577 win32_resource: *Win32Resource,
55825578 progress_node: std.Progress.Node,
55835579) void {
5580 defer comp.link_task_queue.finishPrelinkItem(comp);
55845581 comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) {
55855582 error.AnalysisFail => return,
55865583 else => {
......@@ -5624,6 +5621,7 @@ fn buildRt(
56245621 options: RtOptions,
56255622 out: *?CrtFile,
56265623) void {
5624 defer comp.link_task_queue.finishPrelinkItem(comp);
56275625 comp.buildOutputFromZig(
56285626 root_source_name,
56295627 root_name,
......@@ -5642,6 +5640,7 @@ fn buildRt(
56425640}
56435641
56445642fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.Progress.Node) void {
5643 defer comp.link_task_queue.finishPrelinkItem(comp);
56455644 if (musl.buildCrtFile(comp, crt_file, prog_node)) |_| {
56465645 comp.queued_jobs.musl_crt_file[@intFromEnum(crt_file)] = false;
56475646 } else |err| switch (err) {
......@@ -5653,6 +5652,7 @@ fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.P
56535652}
56545653
56555654fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std.Progress.Node) void {
5655 defer comp.link_task_queue.finishPrelinkItem(comp);
56565656 if (glibc.buildCrtFile(comp, crt_file, prog_node)) |_| {
56575657 comp.queued_jobs.glibc_crt_file[@intFromEnum(crt_file)] = false;
56585658 } else |err| switch (err) {
......@@ -5664,6 +5664,7 @@ fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std
56645664}
56655665
56665666fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5667 defer comp.link_task_queue.finishPrelinkItem(comp);
56675668 if (glibc.buildSharedObjects(comp, prog_node)) |_| {
56685669 // The job should no longer be queued up since it succeeded.
56695670 comp.queued_jobs.glibc_shared_objects = false;
......@@ -5676,6 +5677,7 @@ fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) voi
56765677}
56775678
56785679fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: std.Progress.Node) void {
5680 defer comp.link_task_queue.finishPrelinkItem(comp);
56795681 if (freebsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
56805682 comp.queued_jobs.freebsd_crt_file[@intFromEnum(crt_file)] = false;
56815683 } else |err| switch (err) {
......@@ -5687,6 +5689,7 @@ fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node:
56875689}
56885690
56895691fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5692 defer comp.link_task_queue.finishPrelinkItem(comp);
56905693 if (freebsd.buildSharedObjects(comp, prog_node)) |_| {
56915694 // The job should no longer be queued up since it succeeded.
56925695 comp.queued_jobs.freebsd_shared_objects = false;
......@@ -5699,6 +5702,7 @@ fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) v
56995702}
57005703
57015704fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: std.Progress.Node) void {
5705 defer comp.link_task_queue.finishPrelinkItem(comp);
57025706 if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
57035707 comp.queued_jobs.netbsd_crt_file[@intFromEnum(crt_file)] = false;
57045708 } else |err| switch (err) {
......@@ -5710,6 +5714,7 @@ fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: s
57105714}
57115715
57125716fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
5717 defer comp.link_task_queue.finishPrelinkItem(comp);
57135718 if (netbsd.buildSharedObjects(comp, prog_node)) |_| {
57145719 // The job should no longer be queued up since it succeeded.
57155720 comp.queued_jobs.netbsd_shared_objects = false;
......@@ -5722,6 +5727,7 @@ fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) vo
57225727}
57235728
57245729fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {
5730 defer comp.link_task_queue.finishPrelinkItem(comp);
57255731 if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {
57265732 comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false;
57275733 } else |err| switch (err) {
......@@ -5733,6 +5739,7 @@ fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std
57335739}
57345740
57355741fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void {
5742 defer comp.link_task_queue.finishPrelinkItem(comp);
57365743 if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| {
57375744 comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false;
57385745 } else |err| switch (err) {
......@@ -5744,6 +5751,7 @@ fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_no
57445751}
57455752
57465753fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void {
5754 defer comp.link_task_queue.finishPrelinkItem(comp);
57475755 if (libunwind.buildStaticLib(comp, prog_node)) |_| {
57485756 comp.queued_jobs.libunwind = false;
57495757 } else |err| switch (err) {
......@@ -5753,6 +5761,7 @@ fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void {
57535761}
57545762
57555763fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void {
5764 defer comp.link_task_queue.finishPrelinkItem(comp);
57565765 if (libcxx.buildLibCxx(comp, prog_node)) |_| {
57575766 comp.queued_jobs.libcxx = false;
57585767 } else |err| switch (err) {
......@@ -5762,6 +5771,7 @@ fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void {
57625771}
57635772
57645773fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void {
5774 defer comp.link_task_queue.finishPrelinkItem(comp);
57655775 if (libcxx.buildLibCxxAbi(comp, prog_node)) |_| {
57665776 comp.queued_jobs.libcxxabi = false;
57675777 } else |err| switch (err) {
......@@ -5771,6 +5781,7 @@ fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void {
57715781}
57725782
57735783fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
5784 defer comp.link_task_queue.finishPrelinkItem(comp);
57745785 if (libtsan.buildTsan(comp, prog_node)) |_| {
57755786 comp.queued_jobs.libtsan = false;
57765787 } else |err| switch (err) {
......@@ -5780,6 +5791,7 @@ fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
57805791}
57815792
57825793fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
5794 defer comp.link_task_queue.finishPrelinkItem(comp);
57835795 comp.buildOutputFromZig(
57845796 "c.zig",
57855797 "zigc",
......@@ -7717,6 +7729,7 @@ pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const
77177729/// Only valid to call during `update`. Automatically handles queuing up a
77187730/// linker worker task if there is not already one.
77197731pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) void {
7732 comp.link_prog_node.increaseEstimatedTotalItems(tasks.len);
77207733 comp.link_task_queue.enqueuePrelink(comp, tasks) catch |err| switch (err) {
77217734 error.OutOfMemory => return comp.setAllocFailure(),
77227735 };
......@@ -7789,6 +7802,27 @@ fn getCrtPathsInner(
77897802 };
77907803}
77917804
7805pub 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
77927826/// This decides the optimization mode for all zig-provided libraries, including
77937827/// compiler-rt, libcxx, libc, libunwind, etc.
77947828pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode {
src/InternPool.zig+22-16
......@@ -1137,13 +1137,16 @@ const Local = struct {
11371137 const elem_info = @typeInfo(Elem).@"struct";
11381138 const elem_fields = elem_info.fields;
11391139 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 }
11471150 return @Type(.{ .@"struct" = .{
11481151 .layout = .auto,
11491152 .fields = &new_fields,
......@@ -1158,22 +1161,25 @@ const Local = struct {
11581161 const elem_info = @typeInfo(Elem).@"struct";
11591162 const elem_fields = elem_info.fields;
11601163 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 = .{
11641166 .size = opts.size,
11651167 .is_const = opts.is_const,
11661168 .is_volatile = false,
1167 .alignment = 0,
1169 .alignment = @alignOf(elem_field.type),
11681170 .address_space = .generic,
11691171 .child = elem_field.type,
11701172 .is_allowzero = false,
11711173 .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 }
11771183 return @Type(.{ .@"struct" = .{
11781184 .layout = .auto,
11791185 .fields = &new_fields,
src/Package/Fetch.zig+148-193
......@@ -385,21 +385,23 @@ pub fn run(f: *Fetch) RunError!void {
385385 var resource: Resource = .{ .dir = dir };
386386 return f.runResource(path_or_url, &resource, null);
387387 } else |dir_err| {
388 var server_header_buffer: [init_resource_buffer_size]u8 = undefined;
389
388390 const file_err = if (dir_err == error.NotDir) e: {
389391 if (fs.cwd().openFile(path_or_url, .{})) |file| {
390 var resource: Resource = .{ .file = file };
392 var resource: Resource = .{ .file = file.reader(&server_header_buffer) };
391393 return f.runResource(path_or_url, &resource, null);
392394 } else |err| break :e err;
393395 } else dir_err;
394396
395397 const uri = std.Uri.parse(path_or_url) catch |uri_err| {
396398 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 },
399401 ));
400402 };
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);
403405 return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, null);
404406 }
405407 },
......@@ -464,8 +466,9 @@ pub fn run(f: *Fetch) RunError!void {
464466 f.location_tok,
465467 try eb.printString("invalid URI: {s}", .{@errorName(err)}),
466468 );
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);
469472 return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, remote.hash);
470473}
471474
......@@ -866,8 +869,8 @@ fn fail(f: *Fetch, msg_tok: std.zig.Ast.TokenIndex, msg_str: u32) RunError {
866869}
867870
868871const Resource = union(enum) {
869 file: fs.File,
870 http_request: std.http.Client.Request,
872 file: fs.File.Reader,
873 http_request: HttpRequest,
871874 git: Git,
872875 dir: fs.Dir,
873876
......@@ -877,10 +880,16 @@ const Resource = union(enum) {
877880 want_oid: git.Oid,
878881 };
879882
883 const HttpRequest = struct {
884 request: std.http.Client.Request,
885 response: std.http.Client.Response,
886 buffer: []u8,
887 };
888
880889 fn deinit(resource: *Resource) void {
881890 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(),
884893 .git => |*git_resource| {
885894 git_resource.fetch_stream.deinit();
886895 git_resource.session.deinit();
......@@ -890,21 +899,13 @@ const Resource = union(enum) {
890899 resource.* = undefined;
891900 }
892901
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,
906907 .dir => unreachable,
907 }
908 };
908909 }
909910};
910911
......@@ -967,20 +968,22 @@ const FileType = enum {
967968 }
968969};
969970
970const header_buffer_size = 16 * 1024;
971const init_resource_buffer_size = git.Packet.max_data_length;
971972
972fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Resource {
973fn initResource(f: *Fetch, uri: std.Uri, resource: *Resource, reader_buffer: []u8) RunError!void {
973974 const gpa = f.arena.child_allocator;
974975 const arena = f.arena.allocator();
975976 const eb = &f.error_bundle;
976977
977978 if (ascii.eqlIgnoreCase(uri.scheme, "file")) {
978979 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,
982983 }));
983 } };
984 };
985 resource.* = .{ .file = file.reader(reader_buffer) };
986 return;
984987 }
985988
986989 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
988991 if (ascii.eqlIgnoreCase(uri.scheme, "http") or
989992 ascii.eqlIgnoreCase(uri.scheme, "https"))
990993 {
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})),
10121015 };
10131016
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 ));
10201021
1021 return .{ .http_request = req };
1022 return;
10221023 }
10231024
10241025 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
10261027 {
10271028 var transport_uri = uri;
10281029 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| {
10301031 return f.fail(f.location_tok, try eb.printString(
10311032 "unable to discover remote git server capabilities: {s}",
10321033 .{@errorName(err)},
......@@ -1042,16 +1043,12 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re
10421043 const want_ref_head = try std.fmt.allocPrint(arena, "refs/heads/{s}", .{want_ref});
10431044 const want_ref_tag = try std.fmt.allocPrint(arena, "refs/tags/{s}", .{want_ref});
10441045
1045 var ref_iterator = session.listRefs(.{
1046 var ref_iterator: git.Session.RefIterator = undefined;
1047 session.listRefs(&ref_iterator, .{
10461048 .ref_prefixes = &.{ want_ref, want_ref_head, want_ref_tag },
10471049 .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}));
10551052 defer ref_iterator.deinit();
10561053 while (ref_iterator.next() catch |err| {
10571054 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
10891086
10901087 var want_oid_buf: [git.Oid.max_formatted_length]u8 = undefined;
10911088 _ = 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}));
10971092 };
10981093 errdefer fetch_stream.deinit();
10991094
1100 return .{ .git = .{
1095 resource.* = .{ .git = .{
11011096 .session = session,
11021097 .fetch_stream = fetch_stream,
11031098 .want_oid = want_oid,
11041099 } };
1100 return;
11051101 }
11061102
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}));
11111104}
11121105
11131106fn unpackResource(
......@@ -1121,9 +1114,11 @@ fn unpackResource(
11211114 .file => FileType.fromPath(uri_path) orelse
11221115 return f.fail(f.location_tok, try eb.printString("unknown file type: '{s}'", .{uri_path})),
11231116
1124 .http_request => |req| ft: {
1117 .http_request => |*http_request| ft: {
1118 const head = &http_request.response.head;
1119
11251120 // Content-Type takes first precedence.
1126 const content_type = req.response.content_type orelse
1121 const content_type = head.content_type orelse
11271122 return f.fail(f.location_tok, try eb.addString("missing 'Content-Type' header"));
11281123
11291124 // Extract the MIME type, ignoring charset and boundary directives
......@@ -1165,7 +1160,7 @@ fn unpackResource(
11651160 }
11661161
11671162 // Next, the filename from 'content-disposition: attachment' takes precedence.
1168 if (req.response.content_disposition) |cd_header| {
1163 if (head.content_disposition) |cd_header| {
11691164 break :ft FileType.fromContentDisposition(cd_header) orelse {
11701165 return f.fail(f.location_tok, try eb.printString(
11711166 "unsupported Content-Disposition header value: '{s}' for Content-Type=application/octet-stream",
......@@ -1176,10 +1171,7 @@ fn unpackResource(
11761171
11771172 // Finally, the path from the URI is used.
11781173 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}));
11831175 };
11841176 },
11851177
......@@ -1187,10 +1179,9 @@ fn unpackResource(
11871179
11881180 .dir => |dir| {
11891181 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 }));
11941185 };
11951186 return .{};
11961187 },
......@@ -1198,27 +1189,17 @@ fn unpackResource(
11981189
11991190 switch (file_type) {
12001191 .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());
12041193 },
12051194 .@"tar.gz" => {
1206 var adapter_buffer: [std.crypto.tls.max_ciphertext_record_len]u8 = undefined;
1207 var adapter = resource.reader().adaptToNewApi(&adapter_buffer);
12081195 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);
12101197 return try unpackTarball(f, tmp_directory.handle, &decompress.reader);
12111198 },
12121199 .@"tar.xz" => {
12131200 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}));
12221203 defer dcp.deinit();
12231204 var adapter_buffer: [1024]u8 = undefined;
12241205 var adapter = dcp.reader().adaptToNewApi(&adapter_buffer);
......@@ -1227,9 +1208,7 @@ fn unpackResource(
12271208 .@"tar.zst" => {
12281209 const window_size = std.compress.zstd.default_window_len;
12291210 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, .{
12331212 .verify_checksum = false,
12341213 });
12351214 return try unpackTarball(f, tmp_directory.handle, &decompress.reader);
......@@ -1237,12 +1216,15 @@ fn unpackResource(
12371216 .git_pack => return unpackGitPack(f, tmp_directory.handle, &resource.git) catch |err| switch (err) {
12381217 error.FetchFailed => return error.FetchFailed,
12391218 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},
12431225 )),
1226 else => |e| return e,
12441227 },
1245 .zip => return try unzip(f, tmp_directory.handle, resource.reader()),
12461228 }
12471229}
12481230
......@@ -1277,99 +1259,69 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: *std.Io.Reader) RunError!Un
12771259 return res;
12781260}
12791261
1280fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult {
1262fn unzip(f: *Fetch, out_dir: fs.Dir, reader: *std.Io.Reader) error{ ReadFailed, OutOfMemory, FetchFailed }!UnpackResult {
12811263 // We write the entire contents to a file first because zip files
12821264 // must be processed back to front and they could be too large to
12831265 // load into memory.
12841266
12851267 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.
12971268 const prefix = "tmp/";
12981269 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
13161270 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 };
13401310
13411311 var diagnostics: std.zip.Diagnostics = .{ .allocator = f.arena.allocator() };
13421312 // no need to deinit since we are using an arena allocator
13431313
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}));
13651320
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}));
13701323
1371 const res: UnpackResult = .{ .root_dir = diagnostics.root_dir };
1372 return res;
1324 return .{ .root_dir = diagnostics.root_dir };
13731325}
13741326
13751327fn 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
13871339 var pack_file = try pack_dir.createFile("pkg.pack", .{ .read = true });
13881340 defer pack_file.close();
13891341 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 };
13941349
13951350 var index_file = try pack_dir.createFile("pkg.idx", .{ .read = true });
13961351 defer index_file.close();
src/Package/Fetch/git.zig+166-590
......@@ -11,8 +11,6 @@ const Allocator = mem.Allocator;
1111const Sha1 = std.crypto.hash.Sha1;
1212const Sha256 = std.crypto.hash.sha2.Sha256;
1313const assert = std.debug.assert;
14const zlib = std.compress.zlib;
15const Writer = std.io.Writer;
1614
1715/// The ID of a Git object.
1816pub const Oid = union(Format) {
......@@ -54,6 +52,7 @@ pub const Oid = union(Format) {
5452 };
5553 }
5654
55 // Must be public for use from HashedReader and HashedWriter.
5756 pub fn update(hasher: *Hasher, b: []const u8) void {
5857 switch (hasher.*) {
5958 inline else => |*inner| inner.update(b),
......@@ -65,12 +64,6 @@ pub const Oid = union(Format) {
6564 inline else => |*inner, tag| @unionInit(Oid, @tagName(tag), inner.finalResult()),
6665 };
6766 }
68
69 pub fn writer(hasher: *Hasher, buffer: []u8) Writer {
70 return switch (hasher.*) {
71 inline else => |*inner| inner.writer(buffer),
72 };
73 }
7467 };
7568
7669 const Hashing = union(Format) {
......@@ -107,30 +100,9 @@ pub const Oid = union(Format) {
107100 };
108101 }
109102
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=======
115103 pub fn readBytes(oid_format: Format, reader: *std.Io.Reader) !Oid {
116>>>>>>> origin/flate
117104 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=======
132105 inline else => |tag| @unionInit(Oid, @tagName(tag), (try reader.takeArray(tag.byteLength())).*),
133>>>>>>> origin/flate
134106 };
135107 }
136108
......@@ -430,27 +402,14 @@ const Odb = struct {
430402
431403 /// Reads the object at the current position in the database.
432404 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=======
439405 var base_offset = odb.pack_file.logicalPos();
440>>>>>>> origin/flate
441406 var base_header: EntryHeader = undefined;
442407 var delta_offsets: std.ArrayListUnmanaged(u64) = .empty;
443408 defer delta_offsets.deinit(odb.allocator);
444409 const base_object = while (true) {
445410 if (odb.cache.get(base_offset)) |base_object| break base_object;
446411
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=======
452412 base_header = try EntryHeader.read(odb.format, &odb.pack_file.interface);
453>>>>>>> origin/flate
454413 switch (base_header) {
455414 .ofs_delta => |ofs_delta| {
456415 try delta_offsets.append(odb.allocator, base_offset);
......@@ -460,22 +419,10 @@ const Odb = struct {
460419 .ref_delta => |ref_delta| {
461420 try delta_offsets.append(odb.allocator, base_offset);
462421 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=======
468422 base_offset = odb.pack_file.logicalPos();
469>>>>>>> origin/flate
470423 },
471424 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=======
477425 const base_data = try readObjectRaw(odb.allocator, &odb.pack_file.interface, base_header.uncompressedLength());
478>>>>>>> origin/flate
479426 errdefer odb.allocator.free(base_data);
480427 const base_object: Object = .{ .type = base_header.objectType(), .data = base_data };
481428 try odb.cache.put(odb.allocator, base_offset, base_object);
......@@ -505,14 +452,7 @@ const Odb = struct {
505452 const found_index = while (start_index < end_index) {
506453 const mid_index = start_index + (end_index - start_index) / 2;
507454 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=======
514455 const mid_oid = try Oid.readBytes(odb.format, &odb.index_file.interface);
515>>>>>>> origin/flate
516456 switch (mem.order(u8, mid_oid.slice(), oid.slice())) {
517457 .lt => start_index = mid_index + 1,
518458 .gt => end_index = mid_index,
......@@ -522,28 +462,13 @@ const Odb = struct {
522462
523463 const n_objects = odb.index_header.fan_out_table[255];
524464 const offset_values_start = IndexHeader.size + n_objects * (oid_length + 4);
525 var buffer: [8]u8 = undefined;
526465 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=======
533466 const l1_offset: packed struct { value: u31, big: bool } = @bitCast(try odb.index_file.interface.takeInt(u32, .big));
534>>>>>>> origin/flate
535467 const pack_offset = pack_offset: {
536468 if (l1_offset.big) {
537469 const l2_offset_values_start = offset_values_start + n_objects * 4;
538470 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=======
545471 break :pack_offset try odb.index_file.interface.takeInt(u64, .big);
546>>>>>>> origin/flate
547472 } else {
548473 break :pack_offset l1_offset.value;
549474 }
......@@ -660,17 +585,17 @@ const ObjectCache = struct {
660585/// [protocol-common](https://git-scm.com/docs/protocol-common). The special
661586/// meanings of the delimiter and response-end packets are documented in
662587/// [protocol-v2](https://git-scm.com/docs/protocol-v2).
663const Packet = union(enum) {
588pub const Packet = union(enum) {
664589 flush,
665590 delimiter,
666591 response_end,
667592 data: []const u8,
668593
669 const max_data_length = 65516;
594 pub const max_data_length = 65516;
670595
671596 /// 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;
674599 switch (length) {
675600 0 => return .flush,
676601 1 => return .delimiter,
......@@ -678,13 +603,11 @@ const Packet = union(enum) {
678603 3 => return error.InvalidPacket,
679604 else => if (length - 4 > max_data_length) return error.InvalidPacket,
680605 }
681 const data = buf[0 .. length - 4];
682 try reader.readNoEof(data);
683 return .{ .data = data };
606 return .{ .data = try reader.take(length - 4) };
684607 }
685608
686609 /// 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 {
688611 switch (packet) {
689612 .flush => try writer.writeAll("0000"),
690613 .delimiter => try writer.writeAll("0001"),
......@@ -732,8 +655,10 @@ pub const Session = struct {
732655 allocator: Allocator,
733656 transport: *std.http.Client,
734657 uri: std.Uri,
735 http_headers_buffer: []u8,
658 /// Asserted to be at least `Packet.max_data_length`
659 response_buffer: []u8,
736660 ) !Session {
661 assert(response_buffer.len >= Packet.max_data_length);
737662 var session: Session = .{
738663 .transport = transport,
739664 .location = try .init(allocator, uri),
......@@ -743,7 +668,8 @@ pub const Session = struct {
743668 .allocator = allocator,
744669 };
745670 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);
747673 defer capability_iterator.deinit();
748674 while (try capability_iterator.next()) |capability| {
749675 if (mem.eql(u8, capability.key, "agent")) {
......@@ -818,7 +744,8 @@ pub const Session = struct {
818744 ///
819745 /// The `session.location` is updated if the server returns a redirect, so
820746 /// 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);
822749 var info_refs_uri = session.location.uri;
823750 {
824751 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
......@@ -832,19 +759,22 @@ pub const Session = struct {
832759 info_refs_uri.fragment = null;
833760
834761 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();
845774
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;
848778 const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects;
849779 if (any_redirects_occurred) {
850780 const request_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
......@@ -859,8 +789,7 @@ pub const Session = struct {
859789 session.location = new_location;
860790 }
861791
862 const reader = request.reader();
863 var buf: [Packet.max_data_length]u8 = undefined;
792 it.reader = response.reader(response_buffer);
864793 var state: enum { response_start, response_content } = .response_start;
865794 while (true) {
866795 // Some Git servers (at least GitHub) include an additional
......@@ -870,15 +799,15 @@ pub const Session = struct {
870799 // Thus, we need to skip any such useless additional responses
871800 // before we get the one we're actually looking for. The responses
872801 // 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) {
874803 error.EndOfStream => return error.UnsupportedProtocol, // 'version 2' packet not found
875 else => |other| return other,
804 else => |e| return e,
876805 };
877806 switch (packet) {
878807 .flush => state = .response_start,
879808 .data => |data| switch (state) {
880809 .response_start => if (mem.eql(u8, Packet.normalizeText(data), "version 2")) {
881 return .{ .request = request };
810 return;
882811 } else {
883812 state = .response_content;
884813 },
......@@ -891,7 +820,7 @@ pub const Session = struct {
891820
892821 const CapabilityIterator = struct {
893822 request: std.http.Client.Request,
894 buf: [Packet.max_data_length]u8 = undefined,
823 reader: *std.Io.Reader,
895824
896825 const Capability = struct {
897826 key: []const u8,
......@@ -905,13 +834,13 @@ pub const Session = struct {
905834 }
906835 };
907836
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;
911840 }
912841
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)) {
915844 .flush => return null,
916845 .data => |data| return Capability.parse(Packet.normalizeText(data)),
917846 else => return error.UnexpectedPacket,
......@@ -929,11 +858,13 @@ pub const Session = struct {
929858 include_symrefs: bool = false,
930859 /// Whether to include the peeled object ID for returned tag refs.
931860 include_peeled: bool = false,
932 server_header_buffer: []u8,
861 /// Asserted to be at least `Packet.max_data_length`.
862 buffer: []u8,
933863 };
934864
935865 /// 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);
937868 var upload_pack_uri = session.location.uri;
938869 {
939870 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
......@@ -946,61 +877,56 @@ pub const Session = struct {
946877 upload_pack_uri.query = null;
947878 upload_pack_uri.fragment = null;
948879
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);
953882 if (session.supports_agent) {
954 try Packet.write(.{ .data = agent_capability }, body_writer);
883 try Packet.write(.{ .data = agent_capability }, &body);
955884 }
956885 {
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 });
958889 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);
960891 }
961 try Packet.write(.delimiter, body_writer);
892 try Packet.write(.delimiter, &body);
962893 for (options.ref_prefixes) |ref_prefix| {
963894 const ref_prefix_packet = try std.fmt.allocPrint(session.allocator, "ref-prefix {s}\n", .{ref_prefix});
964895 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);
966897 }
967898 if (options.include_symrefs) {
968 try Packet.write(.{ .data = "symrefs\n" }, body_writer);
899 try Packet.write(.{ .data = "symrefs\n" }, &body);
969900 }
970901 if (options.include_peeled) {
971 try Packet.write(.{ .data = "peel\n" }, body_writer);
902 try Packet.write(.{ .data = "peel\n" }, &body);
972903 }
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,
995915 .format = session.object_format,
996 .request = request,
997916 };
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);
998924 }
999925
1000926 pub const RefIterator = struct {
1001927 format: Oid.Format,
1002928 request: std.http.Client.Request,
1003 buf: [Packet.max_data_length]u8 = undefined,
929 reader: *std.Io.Reader,
1004930
1005931 pub const Ref = struct {
1006932 oid: Oid,
......@@ -1014,13 +940,13 @@ pub const Session = struct {
1014940 iterator.* = undefined;
1015941 }
1016942
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)) {
1019945 .flush => return null,
1020946 .data => |data| {
1021947 const ref_data = Packet.normalizeText(data);
1022948 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;
1024950
1025951 const name_sep_pos = mem.indexOfScalarPos(u8, ref_data, oid_sep_pos + 1, ' ') orelse ref_data.len;
1026952 const name = ref_data[oid_sep_pos + 1 .. name_sep_pos];
......@@ -1034,7 +960,7 @@ pub const Session = struct {
1034960 if (mem.startsWith(u8, attribute, "symref-target:")) {
1035961 symref_target = attribute["symref-target:".len..];
1036962 } 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;
1038964 }
1039965 last_sep_pos = next_sep_pos;
1040966 }
......@@ -1050,9 +976,12 @@ pub const Session = struct {
1050976 /// performed if the server supports it.
1051977 pub fn fetch(
1052978 session: Session,
979 fs: *FetchStream,
1053980 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);
1056985 var upload_pack_uri = session.location.uri;
1057986 {
1058987 const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{
......@@ -1065,65 +994,71 @@ pub const Session = struct {
1065994 upload_pack_uri.query = null;
1066995 upload_pack_uri.fragment = null;
1067996
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);
1072999 if (session.supports_agent) {
1073 try Packet.write(.{ .data = agent_capability }, body_writer);
1000 try Packet.write(.{ .data = agent_capability }, &body);
10741001 }
10751002 {
10761003 const object_format_packet = try std.fmt.allocPrint(session.allocator, "object-format={s}\n", .{@tagName(session.object_format)});
10771004 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);
10791006 }
1080 try Packet.write(.delimiter, body_writer);
1007 try Packet.write(.delimiter, &body);
10811008 // 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);
10831010 // 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);
10851012 if (session.supports_shallow) {
1086 try Packet.write(.{ .data = "deepen 1\n" }, body_writer);
1013 try Packet.write(.{ .data = "deepen 1\n" }, &body);
10871014 }
10881015 for (wants) |want| {
10891016 var buf: [Packet.max_data_length]u8 = undefined;
10901017 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);
10921019 }
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;
11041036 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();
11111037
1112 try request.wait();
1113 if (request.response.status != .ok) return error.ProtocolError;
1038 try request.sendBodyComplete(body.buffered());
11141039
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);
11161044 // We are not interested in any of the sections of the returned fetch
11171045 // data other than the packfile section, since we aren't doing anything
11181046 // complex like ref negotiation (this is a fresh clone).
11191047 var state: enum { section_start, section_content } = .section_start;
11201048 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);
11231050 switch (state) {
11241051 .section_start => switch (packet) {
11251052 .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;
11271062 } else {
11281063 state = .section_content;
11291064 },
......@@ -1140,20 +1075,23 @@ pub const Session = struct {
11401075
11411076 pub const FetchStream = struct {
11421077 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,
11461082
1147 pub fn deinit(stream: *FetchStream) void {
1148 stream.request.deinit();
1083 pub fn deinit(fs: *FetchStream) void {
1084 fs.request.deinit();
11491085 }
11501086
1151 pub const ReadError = std.http.Client.Request.ReadError || error{
1087 pub const Error = error{
11521088 InvalidPacket,
11531089 ProtocolError,
11541090 UnexpectedPacket,
1091 WriteFailed,
1092 ReadFailed,
1093 EndOfStream,
11551094 };
1156 pub const Reader = std.io.GenericReader(*FetchStream, ReadError, read);
11571095
11581096 const StreamCode = enum(u8) {
11591097 pack_data = 1,
......@@ -1162,33 +1100,41 @@ pub const Session = struct {
11621100 _,
11631101 };
11641102
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) {
11711107 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,
11741113 .data => |data| if (data.len > 1) switch (@as(StreamCode, @enumFromInt(data[0]))) {
11751114 .pack_data => {
1176 stream.pos = 1;
1177 stream.len = data.len;
1115 input.toss(1);
1116 fs.remaining_len = data.len;
11781117 break;
11791118 },
1180 .fatal_error => return error.ProtocolError,
1119 .fatal_error => {
1120 fs.err = error.ProtocolError;
1121 return error.ReadFailed;
1122 },
11811123 else => {},
11821124 },
1183 else => return error.UnexpectedPacket,
1125 else => {
1126 fs.err = error.UnexpectedPacket;
1127 return error.ReadFailed;
1128 },
11841129 }
11851130 }
11861131 }
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;
11921138 }
11931139 };
11941140};
......@@ -1199,23 +1145,6 @@ const PackHeader = struct {
11991145 const signature = "PACK";
12001146 const supported_version = 2;
12011147
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=======
12191148 fn read(reader: *std.Io.Reader) !PackHeader {
12201149 const actual_signature = reader.take(4) catch |e| switch (e) {
12211150 error.EndOfStream => return error.InvalidHeader,
......@@ -1226,21 +1155,11 @@ const PackHeader = struct {
12261155 error.EndOfStream => return error.InvalidHeader,
12271156 else => |other| return other,
12281157 };
1229>>>>>>> origin/flate
12301158 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=======
12391159 const total_objects = reader.takeInt(u32, .big) catch |e| switch (e) {
12401160 error.EndOfStream => return error.InvalidHeader,
12411161 else => |other| return other,
12421162 };
1243>>>>>>> origin/flate
12441163 return .{ .total_objects = total_objects };
12451164 }
12461165};
......@@ -1289,30 +1208,13 @@ const EntryHeader = union(Type) {
12891208 };
12901209 }
12911210
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=======
12971211 fn read(format: Oid.Format, reader: *std.Io.Reader) !EntryHeader {
1298>>>>>>> origin/flate
12991212 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=======
13101213 const initial: InitialByte = @bitCast(reader.takeByte() catch |e| switch (e) {
13111214 error.EndOfStream => return error.InvalidFormat,
13121215 else => |other| return other,
13131216 });
13141217 const rest_len = if (initial.has_next) try reader.takeLeb128(u64) else 0;
1315>>>>>>> origin/flate
13161218 var uncompressed_length: u64 = initial.len;
13171219 uncompressed_length |= std.math.shlExact(u64, rest_len, 4) catch return error.InvalidFormat;
13181220 const @"type" = std.enums.fromInt(EntryHeader.Type, initial.type) orelse return error.InvalidFormat;
......@@ -1335,47 +1237,9 @@ const EntryHeader = union(Type) {
13351237 }
13361238};
13371239
1338<<<<<<< HEAD
1339fn readSizeVarInt(r: *std.io.Reader) !u64 {
1340||||||| 733b208256
1341fn readSizeVarInt(r: anytype) !u64 {
1342=======
13431240fn readOffsetVarInt(r: *std.Io.Reader) !u64 {
1344>>>>>>> origin/flate
13451241 const Byte = packed struct { value: u7, has_next: bool };
1346<<<<<<< HEAD
13471242 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
1358fn 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
1373fn 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
13791243 var value: u64 = b.value;
13801244 while (b.has_next) {
13811245 b = @bitCast(try r.takeByte());
......@@ -1392,37 +1256,12 @@ const IndexHeader = struct {
13921256 const supported_version = 2;
13931257 const size = 4 + 4 + @sizeOf([256]u32);
13941258
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=======
14061259 fn read(index_header: *IndexHeader, reader: *std.Io.Reader) !void {
14071260 const sig = try reader.take(4);
14081261 if (!mem.eql(u8, sig, signature)) return error.InvalidHeader;
14091262 const version = try reader.takeInt(u32, .big);
1410>>>>>>> origin/flate
14111263 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=======
14241264 try reader.readSliceEndian(u32, &index_header.fan_out_table, .big);
1425>>>>>>> origin/flate
14261265 }
14271266};
14281267
......@@ -1491,18 +1330,8 @@ pub fn indexPack(
14911330 }
14921331 @memset(fan_out_table[fan_out_index..], count);
14931332
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=======
15031333 var index_hashed_writer = std.Io.Writer.hashed(&index_writer.interface, Oid.Hasher.init(format), &.{});
15041334 const writer = &index_hashed_writer.writer;
1505>>>>>>> origin/flate
15061335 try writer.writeAll(IndexHeader.signature);
15071336 try writer.writeInt(u32, IndexHeader.supported_version, .big);
15081337 for (fan_out_table) |fan_out_entry| {
......@@ -1534,16 +1363,9 @@ pub fn indexPack(
15341363 }
15351364
15361365 try writer.writeAll(pack_checksum.slice());
1537 try writer.flush();
15381366 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=======
15441367 try index_writer.interface.writeAll(index_checksum.slice());
15451368 try index_writer.end();
1546>>>>>>> origin/flate
15471369}
15481370
15491371/// Performs the first pass over the packfile data for index construction.
......@@ -1557,113 +1379,37 @@ fn indexPackFirstPass(
15571379 index_entries: *std.AutoHashMapUnmanaged(Oid, IndexEntry),
15581380 pending_deltas: *std.ArrayListUnmanaged(IndexEntry),
15591381) !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=======
15691382 var flate_buffer: [std.compress.flate.max_window_len]u8 = undefined;
15701383 var pack_buffer: [2048]u8 = undefined; // Reasonably large buffer for file system.
15711384 var pack_hashed = pack.interface.hashed(Oid.Hasher.init(format), &pack_buffer);
1572>>>>>>> origin/flate
15731385
1574<<<<<<< HEAD
1575 const pack_header = try PackHeader.read(&pack_hashed.interface);
1576||||||| 733b208256
1577 const pack_header = try PackHeader.read(pack_reader);
1578=======
15791386 const pack_header = try PackHeader.read(&pack_hashed.reader);
1580>>>>>>> origin/flate
15811387
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=======
15991388 for (0..pack_header.total_objects) |_| {
16001389 const entry_offset = pack.logicalPos() - pack_hashed.reader.bufferedLen();
16011390 const entry_header = try EntryHeader.read(format, &pack_hashed.reader);
1602>>>>>>> origin/flate
16031391 switch (entry_header) {
16041392 .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=======
16151393 var entry_decompress: std.compress.flate.Decompress = .init(&pack_hashed.reader, .zlib, &.{});
16161394 var oid_hasher: Oid.Hashing = .init(format, &flate_buffer);
16171395 const oid_hasher_w = oid_hasher.writer();
1618>>>>>>> origin/flate
16191396 // 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=======
16371397 // part of the object's ID
16381398 try oid_hasher_w.print("{t} {d}\x00", .{ entry_header, object.uncompressed_length });
16391399 const n = try entry_decompress.reader.streamRemaining(oid_hasher_w);
16401400 if (n != object.uncompressed_length) return error.InvalidObject;
16411401 const oid = oid_hasher.final();
16421402 if (!skip_checksums) @compileError("TODO");
1643>>>>>>> origin/flate
16441403 try index_entries.put(allocator, oid, .{
16451404 .offset = entry_offset,
16461405 .crc32 = 0,
16471406 });
16481407 },
16491408 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=======
16621409 var entry_decompress: std.compress.flate.Decompress = .init(&pack_hashed.reader, .zlib, &flate_buffer);
16631410 const n = try entry_decompress.reader.discardRemaining();
16641411 if (n != delta.uncompressed_length) return error.InvalidObject;
16651412 if (!skip_checksums) @compileError("TODO");
1666>>>>>>> origin/flate
16671413 try pending_deltas.append(allocator, .{
16681414 .offset = entry_offset,
16691415 .crc32 = 0,
......@@ -1672,28 +1418,8 @@ fn indexPackFirstPass(
16721418 }
16731419 }
16741420
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=======
16941421 if (!skip_checksums) @compileError("TODO");
16951422 return pack_hashed.hasher.finalResult();
1696>>>>>>> origin/flate
16971423}
16981424
16991425/// Attempts to determine the final object ID of the given deltified object.
......@@ -1738,22 +1464,6 @@ fn indexPackHashDelta(
17381464
17391465 const base_data = try resolveDeltaChain(allocator, format, pack, base_object, delta_offsets.items, cache);
17401466
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=======
17571467 var entry_hasher_buffer: [64]u8 = undefined;
17581468 var entry_hasher: Oid.Hashing = .init(format, &entry_hasher_buffer);
17591469 const entry_hasher_w = entry_hasher.writer();
......@@ -1761,7 +1471,6 @@ fn indexPackHashDelta(
17611471 entry_hasher_w.print("{t} {d}\x00", .{ base_object.type, base_data.len }) catch unreachable;
17621472 entry_hasher_w.writeAll(base_data) catch unreachable;
17631473 return entry_hasher.final();
1764>>>>>>> origin/flate
17651474}
17661475
17671476/// Resolves a chain of deltas, returning the final base object data. `pack` is
......@@ -1783,24 +1492,6 @@ fn resolveDeltaChain(
17831492
17841493 const delta_offset = delta_offsets[i];
17851494 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=======
18041495 const delta_header = try EntryHeader.read(format, &pack.interface);
18051496 const delta_data = try readObjectRaw(allocator, &pack.interface, delta_header.uncompressedLength());
18061497 defer allocator.free(delta_data);
......@@ -1808,24 +1499,12 @@ fn resolveDeltaChain(
18081499 _ = try delta_reader.takeLeb128(u64); // base object size
18091500 const expanded_size = try delta_reader.takeLeb128(u64);
18101501
1811>>>>>>> origin/flate
18121502 const expanded_alloc_size = std.math.cast(usize, expanded_size) orelse return error.ObjectTooLarge;
18131503 const expanded_data = try allocator.alloc(u8, expanded_alloc_size);
18141504 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=======
18251505 var expanded_delta_stream: std.Io.Writer = .fixed(expanded_data);
18261506 try expandDelta(base_data, &delta_reader, &expanded_delta_stream);
18271507 if (expanded_delta_stream.end != expanded_size) return error.InvalidObject;
1828>>>>>>> origin/flate
18291508
18301509 try cache.put(allocator, delta_offset, .{ .type = base_object.type, .data = expanded_data });
18311510 base_data = expanded_data;
......@@ -1833,68 +1512,24 @@ fn resolveDeltaChain(
18331512 return base_data;
18341513}
18351514
1836<<<<<<< HEAD
1837/// Reads the complete contents of an object from `reader`.
1838fn 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.
1843fn readObjectRaw(allocator: Allocator, reader: anytype, size: u64) ![]u8 {
1844=======
18451515/// Reads the complete contents of an object from `reader`. This function may
18461516/// read more bytes than required from `reader`, so the reader position after
18471517/// returning is not reliable.
18481518fn readObjectRaw(allocator: Allocator, reader: *std.Io.Reader, size: u64) ![]u8 {
1849>>>>>>> origin/flate
18501519 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=======
18701520 var aw: std.Io.Writer.Allocating = .init(allocator);
18711521 try aw.ensureTotalCapacity(alloc_size + std.compress.flate.max_window_len);
18721522 defer aw.deinit();
18731523 var decompress: std.compress.flate.Decompress = .init(reader, .zlib, &.{});
18741524 try decompress.reader.streamExact(&aw.writer, alloc_size);
18751525 return aw.toOwnedSlice();
1876>>>>>>> origin/flate
18771526}
18781527
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=======
18851528/// Expands delta data from `delta_reader` to `writer`.
18861529///
1887>>>>>>> origin/flate
18881530/// The format of the delta data is documented in
18891531/// [pack-format](https://git-scm.com/docs/pack-format).
1890<<<<<<< HEAD
1891fn expandDelta(base_object: []const u8, delta_reader: *std.io.Reader, writer: *Writer) !void {
1892 var base_offset: u32 = 0;
1893||||||| 733b208256
1894fn expandDelta(base_object: anytype, delta_reader: anytype, writer: anytype) !void {
1895=======
18961532fn expandDelta(base_object: []const u8, delta_reader: *std.Io.Reader, writer: *std.Io.Writer) !void {
1897>>>>>>> origin/flate
18981533 while (true) {
18991534 const inst: packed struct { value: u7, copy: bool } = @bitCast(delta_reader.takeByte() catch |e| switch (e) {
19001535 error.EndOfStream => return,
......@@ -1916,13 +1551,7 @@ fn expandDelta(base_object: []const u8, delta_reader: *std.Io.Reader, writer: *s
19161551 .offset3 = if (available.offset3) try delta_reader.takeByte() else 0,
19171552 .offset4 = if (available.offset4) try delta_reader.takeByte() else 0,
19181553 };
1919<<<<<<< HEAD
1920 base_offset = @bitCast(offset_parts);
1921||||||| 733b208256
1922 const offset: u32 = @bitCast(offset_parts);
1923=======
19241554 const base_offset: u32 = @bitCast(offset_parts);
1925>>>>>>> origin/flate
19261555 const size_parts: packed struct { size1: u8, size2: u8, size3: u8 } = .{
19271556 .size1 = if (available.size1) try delta_reader.takeByte() else 0,
19281557 .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
19301559 };
19311560 var size: u24 = @bitCast(size_parts);
19321561 if (size == 0) size = 0x10000;
1933<<<<<<< HEAD
1934
19351562 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
19451563 } 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=======
19531564 try delta_reader.streamExact(writer, inst.value);
1954>>>>>>> origin/flate
19551565 } else {
19561566 return error.InvalidDeltaInstruction;
19571567 }
......@@ -1986,16 +1596,9 @@ fn runRepositoryTest(comptime format: Oid.Format, head_commit: []const u8) !void
19861596
19871597 var index_file = try git_dir.dir.createFile("testrepo.idx", .{ .read = true });
19881598 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=======
19951599 var index_file_buffer: [2000]u8 = undefined;
19961600 var index_file_writer = index_file.writer(&index_file_buffer);
19971601 try indexPack(testing.allocator, format, &pack_file_reader, &index_file_writer);
1998>>>>>>> origin/flate
19991602
20001603 // Arbitrary size limit on files read while checking the repository contents
20011604 // (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
20111614 try testing.expectEqualSlices(u8, testrepo_idx, index_file_data);
20121615 }
20131616
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=======
20201617 var index_file_reader = index_file.reader(&index_file_buffer);
20211618 var repository: Repository = undefined;
20221619 try repository.init(testing.allocator, format, &pack_file_reader, &index_file_reader);
2023>>>>>>> origin/flate
20241620 defer repository.deinit();
20251621
20261622 var worktree = testing.tmpDir(.{ .iterate = true });
......@@ -2107,12 +1703,10 @@ test "SHA-256 packfile indexing and checkout" {
21071703/// Checks out a commit of a packfile. Intended for experimenting with and
21081704/// benchmarking possible optimizations to the indexing and checkout behavior.
21091705pub 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;
21131707
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);
21161710 if (args.len != 5) {
21171711 return error.InvalidArguments; // Arguments: format packfile commit worktree
21181712 }
......@@ -2134,34 +1728,16 @@ pub fn main() !void {
21341728 std.debug.print("Starting index...\n", .{});
21351729 var index_file = try git_dir.createFile("idx", .{ .read = true });
21361730 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);
21521734
21531735 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);
21561737 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);
21631739 defer repository.deinit();
2164 var diagnostics: Diagnostics = .{ .allocator = gpa };
1740 var diagnostics: Diagnostics = .{ .allocator = allocator };
21651741 defer diagnostics.deinit();
21661742 try repository.checkout(worktree, commit, &diagnostics);
21671743
src/Sema.zig+60-73
......@@ -2631,7 +2631,7 @@ fn reparentOwnedErrorMsg(
26312631
26322632 const orig_notes = msg.notes.len;
26332633 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]);
26352635 msg.notes[0] = .{
26362636 .src_loc = msg.src_loc,
26372637 .msg = msg.msg,
......@@ -2649,7 +2649,13 @@ pub fn analyzeAsAlign(
26492649 src: LazySrcLoc,
26502650 air_ref: Air.Inst.Ref,
26512651) !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 );
26532659 return sema.validateAlign(block, src, alignment_big);
26542660}
26552661
......@@ -3926,11 +3932,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
39263932 // Whilst constructing our mapping, we will also initialize optional and error union payloads when
39273933 // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters.
39283934 var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len);
3935
39293936 for (stores) |store_inst_idx| {
39303937 const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
39313938 const ptr_to_map = switch (store_inst.tag) {
39323939 .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.
39343941 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
39353942 else => unreachable,
39363943 };
......@@ -4047,13 +4054,12 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
40474054 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
40484055 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| {
40494056 // 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.
40524058 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);
40544060 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx);
40554061 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);
40574063 }
40584064 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
40594065 },
......@@ -8900,6 +8906,14 @@ fn resolveGenericBody(
89008906 return sema.resolveConstDefinedValue(block, src, result, reason);
89018907}
89028908
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`).
89038917pub fn handleExternLibName(
89048918 sema: *Sema,
89058919 block: *Block,
......@@ -8949,6 +8963,11 @@ pub fn handleExternLibName(
89498963 .{ lib_name, lib_name },
89508964 );
89518965 }
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 };
89528971 }
89538972}
89548973
......@@ -14458,8 +14477,8 @@ fn analyzeTupleMul(
1445814477 }
1445914478 }
1446014479 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]);
1446314482 }
1446414483 break :rs runtime_src;
1446514484 };
......@@ -18817,7 +18836,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1881718836 const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
1881818837 const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
1881918838 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);
1882118840 const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" });
1882218841 // Check if this happens to be the lazy alignment of our element type, in
1882318842 // which case we can make this 0 without resolving it.
......@@ -20335,15 +20354,11 @@ fn zirReify(
2033520354 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls),
2033620355 ).?);
2033720356
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)});
2034020359 }
20341
2034220360 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);
2034720362
2034820363 const elem_ty = child_val.toType();
2034920364 if (abi_align != .none) {
......@@ -20920,8 +20935,6 @@ fn reifyUnion(
2092020935 std.hash.autoHash(&hasher, opt_tag_type_val.toIntern());
2092120936 std.hash.autoHash(&hasher, fields_len);
2092220937
20923 var any_aligns = false;
20924
2092520938 for (0..fields_len) |field_idx| {
2092620939 const field_info = try fields_val.elemValue(pt, field_idx);
2092720940
......@@ -20930,16 +20943,11 @@ fn reifyUnion(
2093020943 const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 2));
2093120944
2093220945 const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .union_field_name });
20933
2093420946 std.hash.autoHash(&hasher, .{
2093520947 field_name,
2093620948 field_type_val.toIntern(),
2093720949 field_align_val.toIntern(),
2093820950 });
20939
20940 if (field_align_val.toUnsignedInt(zcu) != 0) {
20941 any_aligns = true;
20942 }
2094320951 }
2094420952
2094520953 const tracked_inst = try block.trackZir(inst);
......@@ -20956,7 +20964,7 @@ fn reifyUnion(
2095620964 true => .safety,
2095720965 false => .none,
2095820966 },
20959 .any_aligned_fields = any_aligns,
20967 .any_aligned_fields = layout != .@"packed",
2096020968 .requires_comptime = .unknown,
2096120969 .assumed_runtime_bits = false,
2096220970 .assumed_pointer_aligned = false,
......@@ -20989,8 +20997,7 @@ fn reifyUnion(
2098920997 );
2099020998 wip_ty.setName(ip, type_name.name, type_name.nav);
2099120999
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);
2099421001
2099521002 const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(zcu)) |tag_type_val| tag_ty: {
2099621003 switch (ip.indexToKey(tag_type_val.toIntern())) {
......@@ -21003,11 +21010,12 @@ fn reifyUnion(
2100321010 const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu);
2100421011 var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len);
2100521012
21006 for (field_types, 0..) |*field_ty, field_idx| {
21013 for (0..fields_len) |field_idx| {
2100721014 const field_info = try fields_val.elemValue(pt, field_idx);
2100821015
2100921016 const field_name_val = try field_info.fieldValue(pt, 0);
2101021017 const field_type_val = try field_info.fieldValue(pt, 1);
21018 const field_alignment_val = try field_info.fieldValue(pt, 2);
2101121019
2101221020 // Don't pass a reason; first loop acts as an assertion that this is valid.
2101321021 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
......@@ -21024,14 +21032,12 @@ fn reifyUnion(
2102421032 }
2102521033 seen_tags.set(enum_index);
2102621034
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);
2103521041 }
2103621042 }
2103721043
......@@ -21055,11 +21061,12 @@ fn reifyUnion(
2105521061 var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
2105621062 try field_names.ensureTotalCapacity(sema.arena, fields_len);
2105721063
21058 for (field_types, 0..) |*field_ty, field_idx| {
21064 for (0..fields_len) |field_idx| {
2105921065 const field_info = try fields_val.elemValue(pt, field_idx);
2106021066
2106121067 const field_name_val = try field_info.fieldValue(pt, 0);
2106221068 const field_type_val = try field_info.fieldValue(pt, 1);
21069 const field_alignment_val = try field_info.fieldValue(pt, 2);
2106321070
2106421071 // Don't pass a reason; first loop acts as an assertion that this is valid.
2106521072 const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
......@@ -21069,14 +21076,12 @@ fn reifyUnion(
2106921076 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)});
2107021077 }
2107121078
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);
2108021085 }
2108121086 }
2108221087
......@@ -21085,7 +21090,7 @@ fn reifyUnion(
2108521090 };
2108621091 errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error
2108721092
21088 for (field_types) |field_ty_ip| {
21093 for (loaded_union.field_types.get(ip)) |field_ty_ip| {
2108921094 const field_ty: Type = .fromInterned(field_ty_ip);
2109021095 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
2109121096 return sema.failWithOwnedErrorMsg(block, msg: {
......@@ -21119,11 +21124,6 @@ fn reifyUnion(
2111921124 }
2112021125 }
2112121126
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 }
2112721127 loaded_union.setTagType(ip, enum_tag_ty);
2112821128 loaded_union.setStatus(ip, .have_field_types);
2112921129
......@@ -21276,7 +21276,6 @@ fn reifyStruct(
2127621276
2127721277 var any_comptime_fields = false;
2127821278 var any_default_inits = false;
21279 var any_aligned_fields = false;
2128021279
2128121280 for (0..fields_len) |field_idx| {
2128221281 const field_info = try fields_val.elemValue(pt, field_idx);
......@@ -21311,11 +21310,6 @@ fn reifyStruct(
2131121310
2131221311 if (field_is_comptime) any_comptime_fields = true;
2131321312 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 }
2131921313 }
2132021314
2132121315 const tracked_inst = try block.trackZir(inst);
......@@ -21327,7 +21321,7 @@ fn reifyStruct(
2132721321 .requires_comptime = .unknown,
2132821322 .any_comptime_fields = any_comptime_fields,
2132921323 .any_default_inits = any_default_inits,
21330 .any_aligned_fields = any_aligned_fields,
21324 .any_aligned_fields = layout != .@"packed",
2133121325 .inits_resolved = true,
2133221326 .key = .{ .reified = .{
2133321327 .zir_index = tracked_inst,
......@@ -21371,21 +21365,14 @@ fn reifyStruct(
2137121365 return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)});
2137221366 }
2137321367
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);
2138921376 }
2139021377
2139121378 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
268268
269269/// These are the modules which we initially queue for analysis in `Compilation.update`.
270270/// `resolveReferences` will use these as the root of its reachability traversal.
271analysis_roots: std.BoundedArray(*Package.Module, 4) = .{},
271analysis_roots_buffer: [4]*Package.Module,
272analysis_roots_len: usize = 0,
272273/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and
273274/// reset to `null` when any semantic analysis occurs (since this invalidates the data).
274275/// Allocated into `gpa`.
......@@ -4013,8 +4014,8 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
40134014 // This is not a sufficient size, but a lower bound.
40144015 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count()));
40154016
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| {
40184019 const file = zcu.module_roots.get(mod).?.unwrap() orelse continue;
40194020 const root_ty = zcu.fileRootType(file);
40204021 if (root_ty == .none) continue;
......@@ -4202,6 +4203,10 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
42024203 return result;
42034204}
42044205
4206pub fn analysisRoots(zcu: *Zcu) []*Package.Module {
4207 return zcu.analysis_roots_buffer[0..zcu.analysis_roots_len];
4208}
4209
42054210pub fn fileByIndex(zcu: *const Zcu, file_index: File.Index) *File {
42064211 return zcu.intern_pool.filePtr(file_index);
42074212}
......@@ -4510,7 +4515,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
45104515 },
45114516 .stage2_spirv => switch (cc) {
45124517 .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,
45144519 else => false,
45154520 },
45164521 };
src/Zcu/PerThread.zig+3-2
......@@ -2116,8 +2116,9 @@ pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool {
21162116 // multi-threaded environment (where things like file indices could differ between compiler runs).
21172117
21182118 // 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| {
21212122 const file_index = zcu.module_roots.get(mod).?.unwrap() orelse continue;
21222123 const file = zcu.fileByIndex(file_index);
21232124
src/codegen/aarch64/Assemble.zig+10-7
......@@ -33,13 +33,16 @@ pub fn nextInstruction(as: *Assemble) !?Instruction {
3333 var symbols: Symbols: {
3434 const symbols = @typeInfo(@TypeOf(instruction.symbols)).@"struct".fields;
3535 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 }
4346 break :Symbols @Type(.{ .@"struct" = .{
4447 .layout = .auto,
4548 .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)
1221012210 },
1221112211 .riscv64_lp64, .riscv32_ilp32 => switch (riscv_c_abi.classifyType(return_type, zcu)) {
1221212212 .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))),
1221612214 .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 });
1221812221 },
1221912222 .byval => return o.lowerType(pt, return_type),
1222012223 .fields => {
src/libs/freebsd.zig-4
......@@ -977,10 +977,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
977977 });
978978}
979979
980pub fn sharedObjectsCount() u8 {
981 return libs.len;
982}
983
984980fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
985981 assert(comp.freebsd_so_files == null);
986982 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
11201120 });
11211121}
11221122
1123pub 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
11351123fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
11361124 const target_version = comp.getTarget().os.versionRange().gnuLibCVersion().?;
11371125
src/libs/mingw.zig+2-1
......@@ -1011,7 +1011,6 @@ const mingw32_winpthreads_src = [_][]const u8{
10111011 "winpthreads" ++ path.sep_str ++ "thread.c",
10121012};
10131013
1014// Note: kernel32 and ntdll are always linked even without targeting MinGW-w64.
10151014pub const always_link_libs = [_][]const u8{
10161015 "api-ms-win-crt-conio-l1-1-0",
10171016 "api-ms-win-crt-convert-l1-1-0",
......@@ -1029,6 +1028,8 @@ pub const always_link_libs = [_][]const u8{
10291028 "api-ms-win-crt-time-l1-1-0",
10301029 "api-ms-win-crt-utility-l1-1-0",
10311030 "advapi32",
1031 "kernel32",
1032 "ntdll",
10321033 "shell32",
10331034 "user32",
10341035};
src/libs/netbsd.zig-4
......@@ -642,10 +642,6 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) anye
642642 });
643643}
644644
645pub fn sharedObjectsCount() u8 {
646 return libs.len;
647}
648
649645fn queueSharedObjects(comp: *Compilation, so_files: BuiltSharedObjects) void {
650646 assert(comp.netbsd_so_files == null);
651647 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 {
138138 defer ordered_nodes.deinit();
139139 try ordered_nodes.ensureTotalCapacityPrecise(self.nodes.items(.is_terminal).len);
140140
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);
151157 }
152 ordered_nodes.appendAssumeCapacity(next_index);
153158 }
154159
155160 var more: bool = true;
src/link/Queue.zig+45-13
......@@ -16,9 +16,9 @@ mutex: std.Thread.Mutex,
1616/// Validates that only one `flushTaskQueue` thread is running at a time.
1717flush_safety: std.debug.SafetyLock,
1818
19/// This is the number of prelink tasks which are expected but have not yet been enqueued.
20/// Guarded by `mutex`.
21pending_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`.
21prelink_wait_count: u32,
2222
2323/// Prelink tasks which have been enqueued and are not yet owned by the worker thread.
2424/// Allocated into `gpa`, guarded by `mutex`.
......@@ -59,7 +59,7 @@ state: union(enum) {
5959 /// The link thread is currently running or queued to run.
6060 running,
6161 /// 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
6363 /// zero, we are specifically waiting for prelink tasks.
6464 finished,
6565 /// 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) {
7373const max_air_bytes_in_flight = 10 * 1024 * 1024;
7474
7575/// 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`.
7777pub const empty: Queue = .{
7878 .mutex = .{},
7979 .flush_safety = .{},
80 .pending_prelink_tasks = 0,
80 .prelink_wait_count = undefined, // set in `start`
8181 .queued_prelink = .empty,
8282 .wip_prelink = .empty,
8383 .queued_zcu = .empty,
......@@ -100,17 +100,49 @@ pub fn deinit(q: *Queue, comp: *Compilation) void {
100100}
101101
102102/// 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.
105104pub fn start(q: *Queue, comp: *Compilation) void {
106105 assert(q.state == .finished);
107106 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;
108110 if (q.queued_prelink.items.len != 0) {
109111 q.state = .running;
110112 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp });
111113 }
112114}
113115
116/// Every call to this must be paired with a call to `finishPrelinkItem`.
117pub 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.
126pub 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
114146/// Called by codegen workers after they have populated a `ZcuTask.LinkFunc.SharedMir`. If the link
115147/// thread was waiting for this MIR, it can resume.
116148pub 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
130162 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, flushTaskQueue, .{ q, comp });
131163}
132164
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.
135167pub fn enqueuePrelink(q: *Queue, comp: *Compilation, tasks: []const PrelinkTask) Allocator.Error!void {
136168 {
137169 q.mutex.lock();
138170 defer q.mutex.unlock();
171 assert(q.prelink_wait_count > 0);
139172 try q.queued_prelink.appendSlice(comp.gpa, tasks);
140 q.pending_prelink_tasks -= @intCast(tasks.len);
141173 switch (q.state) {
142174 .wait_for_mir => unreachable, // we've not started zcu tasks yet
143175 .running => return,
......@@ -167,7 +199,7 @@ pub fn enqueueZcu(q: *Queue, comp: *Compilation, task: ZcuTask) Allocator.Error!
167199 try q.queued_zcu.append(comp.gpa, task);
168200 switch (q.state) {
169201 .running, .wait_for_mir => return,
170 .finished => if (q.pending_prelink_tasks != 0) return,
202 .finished => if (q.prelink_wait_count > 0) return,
171203 }
172204 // Restart the linker thread, unless it would immediately be blocked
173205 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 {
194226 defer q.mutex.unlock();
195227 std.mem.swap(std.ArrayListUnmanaged(PrelinkTask), &q.queued_prelink, &q.wip_prelink);
196228 if (q.wip_prelink.items.len == 0) {
197 if (q.pending_prelink_tasks == 0) {
229 if (q.prelink_wait_count == 0) {
198230 break :prelink; // prelink is done
199231 } else {
200232 // 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 {
312312 return jitCmd(gpa, arena, cmd_args, .{
313313 .cmd_name = "resinator",
314314 .root_src_path = "resinator/main.zig",
315 .windows_libs = &.{"advapi32"},
316315 .depend_on_aro = true,
317316 .prepend_zig_lib_dir_path = true,
318317 .server = use_server,
......@@ -337,7 +336,6 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
337336 return jitCmd(gpa, arena, cmd_args, .{
338337 .cmd_name = "std",
339338 .root_src_path = "std-docs.zig",
340 .windows_libs = &.{"ws2_32"},
341339 .prepend_zig_lib_dir_path = true,
342340 .prepend_zig_exe_path = true,
343341 .prepend_global_cache_path = true,
......@@ -3659,6 +3657,7 @@ fn buildOutputType(
36593657 } else if (target.os.tag == .windows) {
36603658 try test_exec_args.appendSlice(arena, &.{
36613659 "--subsystem", "console",
3660 "-lkernel32", "-lntdll",
36623661 });
36633662 }
36643663
......@@ -3862,8 +3861,7 @@ fn createModule(
38623861 .only_compiler_rt => continue,
38633862 }
38643863
3865 // We currently prefer import libraries provided by MinGW-w64 even for MSVC.
3866 if (target.os.tag == .windows) {
3864 if (target.isMinGW()) {
38673865 const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| {
38683866 fatal("failed to check zig installation for DLL import libs: {s}", .{
38693867 @errorName(err),
......@@ -4796,7 +4794,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
47964794 writeSimpleTemplateFile(Package.Manifest.basename,
47974795 \\.{{
47984796 \\ .name = .{s},
4799 \\ .version = "{s}",
4797 \\ .version = "0.0.1",
4798 \\ .minimum_zig_version = "{s}",
48004799 \\ .paths = .{{""}},
48014800 \\ .fingerprint = 0x{x},
48024801 \\}}
......@@ -4811,6 +4810,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
48114810 };
48124811 writeSimpleTemplateFile(Package.build_zig_basename,
48134812 \\const std = @import("std");
4813 \\
48144814 \\pub fn build(b: *std.Build) void {{
48154815 \\ _ = b; // stub
48164816 \\}}
......@@ -4891,6 +4891,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
48914891 var fetch_mode: Package.Fetch.JobQueue.Mode = .needed;
48924892 var system_pkg_dir_path: ?[]const u8 = null;
48934893 var debug_target: ?[]const u8 = null;
4894 var debug_libc_paths_file: ?[]const u8 = null;
48944895
48954896 const argv_index_exe = child_argv.items.len;
48964897 _ = try child_argv.addOne();
......@@ -5014,6 +5015,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
50145015 } else {
50155016 warn("Zig was compiled without debug extensions. --debug-target has no effect.", .{});
50165017 }
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 }
50175026 } else if (mem.eql(u8, arg, "--verbose-link")) {
50185027 verbose_link = true;
50195028 } else if (mem.eql(u8, arg, "--verbose-cc")) {
......@@ -5101,6 +5110,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
51015110 .is_explicit_dynamic_linker = false,
51025111 };
51035112 };
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 };
51045121
51055122 process.raiseFileDescriptorLimit();
51065123
......@@ -5356,15 +5373,8 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
53565373
53575374 try root_mod.deps.put(arena, "@build", build_mod);
53585375
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
53675376 const comp = Compilation.create(gpa, arena, .{
5377 .libc_installation = libc_installation,
53685378 .dirs = dirs,
53695379 .root_name = "build",
53705380 .config = config,
......@@ -5385,7 +5395,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
53855395 .cache_mode = .whole,
53865396 .reference_trace = reference_trace,
53875397 .debug_compile_errors = debug_compile_errors,
5388 .windows_lib_names = windows_libs.keys(),
53895398 }) catch |err| {
53905399 fatal("unable to create compilation: {s}", .{@errorName(err)});
53915400 };
......@@ -5489,7 +5498,6 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
54895498const JitCmdOptions = struct {
54905499 cmd_name: []const u8,
54915500 root_src_path: []const u8,
5492 windows_libs: []const []const u8 = &.{},
54935501 prepend_zig_lib_dir_path: bool = false,
54945502 prepend_global_cache_path: bool = false,
54955503 prepend_zig_exe_path: bool = false,
......@@ -5606,13 +5614,6 @@ fn jitCmd(
56065614 try root_mod.deps.put(arena, "aro", aro_mod);
56075615 }
56085616
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
56165617 const comp = Compilation.create(gpa, arena, .{
56175618 .dirs = dirs,
56185619 .root_name = options.cmd_name,
......@@ -5623,7 +5624,6 @@ fn jitCmd(
56235624 .self_exe_path = self_exe_path,
56245625 .thread_pool = &thread_pool,
56255626 .cache_mode = .whole,
5626 .windows_lib_names = windows_libs.keys(),
56275627 }) catch |err| {
56285628 fatal("unable to create compilation: {s}", .{@errorName(err)});
56295629 };
src/target.zig+1-1
......@@ -20,7 +20,7 @@ pub fn cannotDynamicLink(target: *const std.Target) bool {
2020/// Similarly on FreeBSD and NetBSD we always link system libc
2121/// since this is the stable syscall interface.
2222pub fn osRequiresLibC(target: *const std.Target) bool {
23 return target.os.requiresLibC();
23 return target.requiresLibC();
2424}
2525
2626pub 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" {
318318test "zero sized struct in tuple handled correctly" {
319319 const State = struct {
320320 const Self = @This();
321 const Inner = struct {};
322
321323 data: @Type(.{
322324 .@"struct" = .{
323325 .is_tuple = true,
......@@ -325,10 +327,10 @@ test "zero sized struct in tuple handled correctly" {
325327 .decls = &.{},
326328 .fields = &.{.{
327329 .name = "0",
328 .type = struct {},
330 .type = Inner,
329331 .default_value_ptr = null,
330332 .is_comptime = false,
331 .alignment = 0,
333 .alignment = @alignOf(Inner),
332334 }},
333335 },
334336 }),
test/behavior/type.zig+3-3
......@@ -433,8 +433,8 @@ test "Type.Union" {
433433 .layout = .@"packed",
434434 .tag_type = null,
435435 .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 },
438438 },
439439 .decls = &.{},
440440 },
......@@ -735,7 +735,7 @@ test "struct field names sliced at comptime from larger string" {
735735 var it = std.mem.tokenizeScalar(u8, text, '\n');
736736 while (it.next()) |name| {
737737 fields = fields ++ &[_]Type.StructField{.{
738 .alignment = 0,
738 .alignment = @alignOf(usize),
739739 .name = name ++ "",
740740 .type = usize,
741741 .default_value_ptr = null,
test/behavior/union.zig+8
......@@ -2311,3 +2311,11 @@ test "set mutable union by switching on same union" {
23112311 try expect(val == .bar);
23122312 try expect(val.bar == 2);
23132313}
2314
2315test "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 @@
1pub var global_var: i32 align(0) = undefined;
1var global_var: i32 align(0) = undefined;
22
3pub export fn a() void {
3export fn a() void {
44 _ = &global_var;
55}
66
7pub extern var extern_var: i32 align(0);
7extern var extern_var: i32 align(0);
88
9pub export fn b() void {
9export fn b() void {
1010 _ = &extern_var;
1111}
1212
13pub export fn c() align(0) void {}
13export fn c() align(0) void {}
1414
15pub export fn d() void {
15export fn d() void {
1616 _ = *align(0) fn () i32;
1717}
1818
19pub export fn e() void {
19export fn e() void {
2020 var local_var: i32 align(0) = undefined;
2121 _ = &local_var;
2222}
2323
24pub export fn f() void {
24export fn f() void {
2525 _ = *align(0) i32;
2626}
2727
28pub export fn g() void {
28export fn g() void {
2929 _ = []align(0) i32;
3030}
3131
32pub export fn h() void {
32export fn h() void {
3333 _ = struct { field: i32 align(0) };
3434}
3535
36pub export fn i() void {
36export fn i() void {
3737 _ = union { field: i32 align(0) };
3838}
3939
40export 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
55export 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
4068// error
41// backend=stage2
42// target=native
4369//
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
4773// :16:16: error: alignment must be >= 1
4874// :20:30: error: alignment must be >= 1
4975// :25:16: error: alignment must be >= 1
5076// :29:17: error: alignment must be >= 1
5177// :33:35: error: alignment must be >= 1
5278// :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 {
1111// backend=stage2
1212// target=native
1313//
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 @@
1export 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 @@
1const 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 {
7575// :16:5: error: tuple field name '3' does not match field index 0
7676// :30:5: error: comptime field without default initialization value
7777// :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 {
4343
4444// error
4545//
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(
593593pub const CaseTestOptions = struct {
594594 test_filters: []const []const u8,
595595 test_target_filters: []const []const u8,
596 skip_compile_errors: bool,
596597 skip_non_native: bool,
597598 skip_freebsd: bool,
598599 skip_netbsd: bool,
......@@ -618,6 +619,8 @@ pub fn lowerToBuildSteps(
618619 if (std.mem.indexOf(u8, case.name, test_filter)) |_| break;
619620 } else if (options.test_filters.len > 0) continue;
620621
622 if (case.case.? == .Error and options.skip_compile_errors) continue;
623
621624 if (options.skip_non_native and !case.target.query.isNative())
622625 continue;
623626
test/standalone/child_process/build.zig+11
......@@ -31,5 +31,16 @@ pub fn build(b: *std.Build) void {
3131 run.addArtifactArg(child);
3232 run.expectExitCode(0);
3333
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
3444 test_step.dependOn(&run.step);
45 test_step.dependOn(&run_relative.step);
3546}
test/standalone/child_process/main.zig+14-2
......@@ -11,7 +11,14 @@ pub fn main() !void {
1111 var it = try std.process.argsWithAllocator(gpa);
1212 defer it.deinit();
1313 _ = 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);
1522
1623 var child = std.process.Child.init(&.{ child_path, "hello arg" }, gpa);
1724 child.stdin_behavior = .Pipe;
......@@ -39,7 +46,12 @@ pub fn main() !void {
3946 },
4047 else => |term| testError("abnormal child exit: {}", .{term}),
4148 }
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} }));
4355}
4456
4557var parent_test_error = false;
test/standalone/simple/build.zig-8
......@@ -50,10 +50,6 @@ pub fn build(b: *std.Build) void {
5050 });
5151 if (case.link_libc) exe.root_module.link_libc = true;
5252
53 if (resolved_target.result.os.tag == .windows) {
54 exe.root_module.linkSystemLibrary("advapi32", .{});
55 }
56
5753 _ = exe.getEmittedBin();
5854
5955 step.dependOn(&exe.step);
......@@ -70,10 +66,6 @@ pub fn build(b: *std.Build) void {
7066 });
7167 if (case.link_libc) exe.root_module.link_libc = true;
7268
73 if (resolved_target.result.os.tag == .windows) {
74 exe.root_module.linkSystemLibrary("advapi32", .{});
75 }
76
7769 const run = b.addRunArtifact(exe);
7870 step.dependOn(&run.step);
7971 }
test/standalone/windows_argv/build.zig-2
......@@ -47,8 +47,6 @@ pub fn build(b: *std.Build) !void {
4747 }),
4848 });
4949
50 fuzz.root_module.linkSystemLibrary("advapi32", .{});
51
5250 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;
5351 const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom");
5452
test/standalone/windows_bat_args/build.zig-4
......@@ -28,8 +28,6 @@ pub fn build(b: *std.Build) !void {
2828 }),
2929 });
3030
31 test_exe.root_module.linkSystemLibrary("advapi32", .{});
32
3331 const run = b.addRunArtifact(test_exe);
3432 run.addArtifactArg(echo_args);
3533 run.expectExitCode(0);
......@@ -46,8 +44,6 @@ pub fn build(b: *std.Build) !void {
4644 }),
4745 });
4846
49 fuzz.root_module.linkSystemLibrary("advapi32", .{});
50
5147 const fuzz_max_iterations = b.option(u64, "iterations", "The max fuzz iterations (default: 100)") orelse 100;
5248 const fuzz_iterations_arg = std.fmt.allocPrint(b.allocator, "{}", .{fuzz_max_iterations}) catch @panic("oom");
5349
test/standalone/windows_spawn/build.zig-2
......@@ -28,8 +28,6 @@ pub fn build(b: *std.Build) void {
2828 }),
2929 });
3030
31 main.root_module.linkSystemLibrary("advapi32", .{});
32
3331 const run = b.addRunArtifact(main);
3432 run.addArtifactArg(hello);
3533 run.expectExitCode(0);
test/standalone/windows_spawn/main.zig+46
......@@ -1,4 +1,5 @@
11const std = @import("std");
2
23const windows = std.os.windows;
34const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
45
......@@ -39,6 +40,9 @@ pub fn main() anyerror!void {
3940 // No PATH, so it should fail to find anything not in the cwd
4041 try testExecError(error.FileNotFound, allocator, "something_missing");
4142
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
4246 std.debug.assert(windows.kernel32.SetEnvironmentVariableW(
4347 utf16Literal("PATH"),
4448 tmp_absolute_path_w,
......@@ -149,6 +153,48 @@ pub fn main() anyerror!void {
149153 // If we try to exec but provide a cwd that is an absolute path, the PATH
150154 // should still be searched and the goodbye.exe in something should be found.
151155 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");
152198}
153199
154200fn testExecError(err: anyerror, allocator: std.mem.Allocator, command: []const u8) !void {
test/tests.zig-9
......@@ -2238,7 +2238,6 @@ const ModuleTestOptions = struct {
22382238 desc: []const u8,
22392239 optimize_modes: []const OptimizeMode,
22402240 include_paths: []const []const u8,
2241 windows_libs: []const []const u8,
22422241 skip_single_threaded: bool,
22432242 skip_non_native: bool,
22442243 skip_freebsd: bool,
......@@ -2373,10 +2372,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
23732372
23742373 for (options.include_paths) |include_path| these_tests.root_module.addIncludePath(b.path(include_path));
23752374
2376 if (target.os.tag == .windows) {
2377 for (options.windows_libs) |lib| these_tests.root_module.linkSystemLibrary(lib, .{});
2378 }
2379
23802375 const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}{s}", .{
23812376 options.name,
23822377 triple_txt,
......@@ -2672,10 +2667,6 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step) !void {
26722667 }),
26732668 });
26742669
2675 if (b.graph.host.result.os.tag == .windows) {
2676 incr_check.root_module.linkSystemLibrary("advapi32", .{});
2677 }
2678
26792670 var dir = try b.build_root.handle.openDir("test/incremental", .{ .iterate = true });
26802671 defer dir.close();
26812672