authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-20 13:46:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:27-07:00
loge9fd9798f4d6e3cc5af6179881e0323f983095b5
treeb5dd76d64fe2e7cca2e2f9e0eb1917a5bb1e3624
parent24441b184f989dc889c33b6422ec5ddd8f385c5e

std.tar.Writer: update reader/writer API usage


7 files changed, 179 insertions(+), 141 deletions(-)

lib/compiler/std-docs.zig+30-20
...@@ -202,34 +202,33 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void {...@@ -202,34 +202,33 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void {
202 var walker = try std_dir.walk(gpa);202 var walker = try std_dir.walk(gpa);
203 defer walker.deinit();203 defer walker.deinit();
204204
205 var archiver = std.tar.writer(response.writer());205 var tar_buffer: [@sizeOf(std.tar.Writer.Header)]u8 = undefined;
206 archiver.prefix = "std";206 var response_bw = response.writer().buffered(&tar_buffer);
207 var tar_writer: std.tar.Writer = .{ .underlying_writer = &response_bw };
208 tar_writer.prefix = "std";
207209
208 while (try walker.next()) |entry| {210 while (try walker.next()) |entry| {
209 switch (entry.kind) {211 switch (entry.kind) {
210 .file => {212 .file => {
211 if (!std.mem.endsWith(u8, entry.basename, ".zig"))213 if (!std.mem.endsWith(u8, entry.basename, ".zig")) continue;
212 continue;214 if (std.mem.endsWith(u8, entry.basename, "test.zig")) continue;
213 if (std.mem.endsWith(u8, entry.basename, "test.zig"))
214 continue;
215 },215 },
216 else => continue,216 else => continue,
217 }217 }
218 var file = try entry.dir.openFile(entry.basename, .{});218 var file = try entry.dir.openFile(entry.basename, .{});
219 defer file.close();219 defer file.close();
220 try archiver.writeFile(entry.path, file);220 const stat = try file.stat();
221 try tar_writer.writeFile(entry.path, file, stat);
221 }222 }
222223
223 {224 {
224 // Since this command is JIT compiled, the builtin module available in225 // Since this command is JIT compiled, the builtin module available in
225 // this source file corresponds to the user's host system.226 // this source file corresponds to the user's host system.
226 const builtin_zig = @embedFile("builtin");227 const builtin_zig = @embedFile("builtin");
227 archiver.prefix = "builtin";228 tar_writer.prefix = "builtin";
228 try archiver.writeFileBytes("builtin.zig", builtin_zig, .{});229 try tar_writer.writeFileBytes("builtin.zig", builtin_zig, .{});
229 }230 }
230231
231 // intentionally omitting the pointless trailer
232 //try archiver.finish();
233 try response.end();232 try response.end();
234}233}
235234
...@@ -255,16 +254,27 @@ fn serveWasm(...@@ -255,16 +254,27 @@ fn serveWasm(
255 }) catch unreachable) catch unreachable),254 }) catch unreachable) catch unreachable),
256 .output_mode = .Exe,255 .output_mode = .Exe,
257 });256 });
258 // std.http.Server does not have a sendfile API yet.
259 const bin_path = try wasm_base_path.join(arena, bin_name);257 const bin_path = try wasm_base_path.join(arena, bin_name);
260 const file_contents = try bin_path.root_dir.handle.readFileAlloc(gpa, bin_path.sub_path, 10 * 1024 * 1024);258 const file = try bin_path.root_dir.handle.openFile(bin_path.sub_path, .{});
261 defer gpa.free(file_contents);259 defer file.close();
262 try request.respond(file_contents, .{260 const content_length = std.math.cast(usize, (try file.stat()).size) orelse return error.FileTooBig;
263 .extra_headers = &.{261
264 .{ .name = "content-type", .value = "application/wasm" },262 var response = try request.respondStreaming(.{
265 cache_control_header,263 .content_length = content_length,
264 .respond_options = .{
265 .extra_headers = &.{
266 .{ .name = "content-type", .value = "application/wasm" },
267 cache_control_header,
268 },
266 },269 },
267 });270 });
271
272 var bw = response.writer().unbuffered();
273 try bw.writeFileAll(file, .{
274 .offset = .zero,
275 .limit = .limited(content_length),
276 });
277 try response.end();
268}278}
269279
270const autodoc_root_name = "autodoc";280const autodoc_root_name = "autodoc";
...@@ -396,8 +406,8 @@ fn receiveWasmMessage(...@@ -396,8 +406,8 @@ fn receiveWasmMessage(
396 },406 },
397 .error_bundle => {407 .error_bundle => {
398 const eb_hdr = try br.takeStructEndian(std.zig.Server.Message.ErrorBundle, .little);408 const eb_hdr = try br.takeStructEndian(std.zig.Server.Message.ErrorBundle, .little);
399 const extra_array = try br.readArrayEndianAlloc(arena, u32, eb_hdr.extra_len, .little);409 const extra_array = try br.readSliceEndianAlloc(arena, u32, eb_hdr.extra_len, .little);
400 const string_bytes = try br.readAlloc(arena, eb_hdr.string_bytes_len);410 const string_bytes = try br.readSliceAlloc(arena, eb_hdr.string_bytes_len);
401 result_error_bundle.* = .{411 result_error_bundle.* = .{
402 .string_bytes = string_bytes,412 .string_bytes = string_bytes,
403 .extra = extra_array,413 .extra = extra_array,
lib/std/compress/flate/inflate.zig+1-1
...@@ -704,7 +704,7 @@ pub fn BitReader(comptime T: type) type {...@@ -704,7 +704,7 @@ pub fn BitReader(comptime T: type) type {
704 n += 1;704 n += 1;
705 }705 }
706 // Then use forward reader for all other bytes.706 // Then use forward reader for all other bytes.
707 try self.forward_reader.read(buf[n..]);707 try self.forward_reader.readSlice(buf[n..]);
708 }708 }
709709
710 /// Alias for readF(U, 0).710 /// Alias for readF(U, 0).
lib/std/io/BufferedReader.zig+18-7
...@@ -51,6 +51,19 @@ pub fn readVec(br: *BufferedReader, data: []const []u8) Reader.Error!usize {...@@ -51,6 +51,19 @@ pub fn readVec(br: *BufferedReader, data: []const []u8) Reader.Error!usize {
51 return passthruReadVec(br, data);51 return passthruReadVec(br, data);
52}52}
5353
54pub fn read(br: *BufferedReader, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
55 return passthruRead(br, bw, limit);
56}
57
58/// "Pump" data from the reader to the writer.
59pub fn readAll(br: *BufferedReader, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!void {
60 var remaining = limit;
61 while (true) {
62 const n = try passthruRead(br, bw, remaining);
63 remaining = remaining.subtract(n).?;
64 }
65}
66
54fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {67fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
55 const br: *BufferedReader = @alignCast(@ptrCast(ctx));68 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
56 const buffer = br.buffer[0..br.end];69 const buffer = br.buffer[0..br.end];
...@@ -134,7 +147,6 @@ pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) !void {...@@ -134,7 +147,6 @@ pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) !void {
134///147///
135/// See also:148/// See also:
136/// * `peek`149/// * `peek`
137/// * `tryPeekArray`
138/// * `toss`150/// * `toss`
139pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {151pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {
140 assert(n <= br.buffer.len);152 assert(n <= br.buffer.len);
...@@ -155,7 +167,6 @@ pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {...@@ -155,7 +167,6 @@ pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {
155///167///
156/// See also:168/// See also:
157/// * `peek`169/// * `peek`
158/// * `tryPeekGreedy`
159/// * `toss`170/// * `toss`
160pub fn peekGreedy(br: *BufferedReader, n: usize) Reader.Error![]u8 {171pub fn peekGreedy(br: *BufferedReader, n: usize) Reader.Error![]u8 {
161 assert(n <= br.buffer.len);172 assert(n <= br.buffer.len);
...@@ -280,7 +291,7 @@ pub fn discardRemaining(br: *BufferedReader) Reader.ShortError!usize {...@@ -280,7 +291,7 @@ pub fn discardRemaining(br: *BufferedReader) Reader.ShortError!usize {
280///291///
281/// See also:292/// See also:
282/// * `peek`293/// * `peek`
283pub fn read(br: *BufferedReader, buffer: []u8) Reader.Error!void {294pub fn readSlice(br: *BufferedReader, buffer: []u8) Reader.Error!void {
284 const in_buffer = br.buffer[br.seek..br.end];295 const in_buffer = br.buffer[br.seek..br.end];
285 const copy_len = @min(buffer.len, in_buffer.len);296 const copy_len = @min(buffer.len, in_buffer.len);
286 @memcpy(buffer[0..copy_len], in_buffer[0..copy_len]);297 @memcpy(buffer[0..copy_len], in_buffer[0..copy_len]);
...@@ -313,7 +324,7 @@ pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize {...@@ -313,7 +324,7 @@ pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize {
313324
314/// The function is inline to avoid the dead code in case `endian` is325/// The function is inline to avoid the dead code in case `endian` is
315/// comptime-known and matches host endianness.326/// comptime-known and matches host endianness.
316pub inline fn readArrayEndianAlloc(327pub inline fn readSliceEndianAlloc(
317 br: *BufferedReader,328 br: *BufferedReader,
318 allocator: Allocator,329 allocator: Allocator,
319 Elem: type,330 Elem: type,
...@@ -322,17 +333,17 @@ pub inline fn readArrayEndianAlloc(...@@ -322,17 +333,17 @@ pub inline fn readArrayEndianAlloc(
322) ReadAllocError![]Elem {333) ReadAllocError![]Elem {
323 const dest = try allocator.alloc(Elem, len);334 const dest = try allocator.alloc(Elem, len);
324 errdefer allocator.free(dest);335 errdefer allocator.free(dest);
325 try read(br, @ptrCast(dest));336 try readSlice(br, @ptrCast(dest));
326 if (native_endian != endian) std.mem.byteSwapAllFields(Elem, dest);337 if (native_endian != endian) std.mem.byteSwapAllFields(Elem, dest);
327 return dest;338 return dest;
328}339}
329340
330pub const ReadAllocError = Reader.Error || Allocator.Error;341pub const ReadAllocError = Reader.Error || Allocator.Error;
331342
332pub fn readAlloc(br: *BufferedReader, allocator: Allocator, len: usize) ReadAllocError![]u8 {343pub fn readSliceAlloc(br: *BufferedReader, allocator: Allocator, len: usize) ReadAllocError![]u8 {
333 const dest = try allocator.alloc(u8, len);344 const dest = try allocator.alloc(u8, len);
334 errdefer allocator.free(dest);345 errdefer allocator.free(dest);
335 try read(br, dest);346 try readSlice(br, dest);
336 return dest;347 return dest;
337}348}
338349
lib/std/io/BufferedWriter.zig+6-6
...@@ -453,24 +453,24 @@ pub inline fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std...@@ -453,24 +453,24 @@ pub inline fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std
453 }453 }
454}454}
455455
456pub inline fn writeArrayEndian(456pub inline fn writeSliceEndian(
457 bw: *BufferedWriter,457 bw: *BufferedWriter,
458 Elem: type,458 Elem: type,
459 array: []const Elem,459 slice: []const Elem,
460 endian: std.builtin.Endian,460 endian: std.builtin.Endian,
461) Writer.Error!void {461) Writer.Error!void {
462 if (native_endian == endian) {462 if (native_endian == endian) {
463 return writeAll(bw, @ptrCast(array));463 return writeAll(bw, @ptrCast(slice));
464 } else {464 } else {
465 return bw.writeArraySwap(bw, Elem, array);465 return bw.writeArraySwap(bw, Elem, slice);
466 }466 }
467}467}
468468
469/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`469/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
470pub fn writeArraySwap(bw: *BufferedWriter, Elem: type, array: []const Elem) Writer.Error!void {470pub fn writeSliceSwap(bw: *BufferedWriter, Elem: type, slice: []const Elem) Writer.Error!void {
471 // copy to storage first, then swap in place471 // copy to storage first, then swap in place
472 _ = bw;472 _ = bw;
473 _ = array;473 _ = slice;
474 @panic("TODO");474 @panic("TODO");
475}475}
476476
lib/std/io/Reader.zig+11-3
...@@ -45,18 +45,26 @@ pub const VTable = struct {...@@ -45,18 +45,26 @@ pub const VTable = struct {
45 discard: *const fn (context: ?*anyopaque, limit: Limit) Error!usize,45 discard: *const fn (context: ?*anyopaque, limit: Limit) Error!usize,
46};46};
4747
48pub const RwError = RwAllError || error{48pub const RwError = error{
49 /// See the `Reader` implementation for detailed diagnostics.
50 ReadFailed,
51 /// See the `Writer` implementation for detailed diagnostics.
52 WriteFailed,
49 /// End of stream indicated from the `Reader`. This error cannot originate53 /// End of stream indicated from the `Reader`. This error cannot originate
50 /// from the `Writer`.54 /// from the `Writer`.
51 EndOfStream,55 EndOfStream,
52};56};
5357
54pub const Error = ShortError || error{58pub const Error = error{
59 /// See the `Reader` implementation for detailed diagnostics.
60 ReadFailed,
55 EndOfStream,61 EndOfStream,
56};62};
5763
58/// For functions that handle end of stream as a success case.64/// For functions that handle end of stream as a success case.
59pub const RwAllError = ShortError || error{65pub const RwAllError = error{
66 /// See the `Reader` implementation for detailed diagnostics.
67 ReadFailed,
60 /// See the `Writer` implementation for detailed diagnostics.68 /// See the `Writer` implementation for detailed diagnostics.
61 WriteFailed,69 WriteFailed,
62};70};
lib/std/tar/Writer.zig+110-101
...@@ -4,7 +4,6 @@ const testing = std.testing;...@@ -4,7 +4,6 @@ const testing = std.testing;
4const Writer = @This();4const Writer = @This();
55
6const block_size = @sizeOf(Header);6const block_size = @sizeOf(Header);
7const empty_block: [block_size]u8 = [_]u8{0} ** block_size;
87
9/// Options for writing file/dir/link. If left empty 0o664 is used for8/// Options for writing file/dir/link. If left empty 0o664 is used for
10/// file mode and current time for mtime.9/// file mode and current time for mtime.
...@@ -14,80 +13,91 @@ pub const Options = struct {...@@ -14,80 +13,91 @@ pub const Options = struct {
14 /// File system modification time.13 /// File system modification time.
15 mtime: u64 = 0,14 mtime: u64 = 0,
16};15};
17const Self = @This();
1816
19underlying_writer: *std.io.BufferedWriter,17underlying_writer: *std.io.BufferedWriter,
20prefix: []const u8 = "",18prefix: []const u8 = "",
21mtime_now: u64 = 0,19mtime_now: u64 = 0,
2220
21const Error = error{
22 WriteFailed,
23 OctalOverflow,
24 NameTooLong,
25};
26
23/// Sets prefix for all other write* method paths.27/// Sets prefix for all other write* method paths.
24pub fn setRoot(self: *Self, root: []const u8) !void {28pub fn setRoot(w: *Writer, root: []const u8) Error!void {
25 if (root.len > 0)29 if (root.len > 0)
26 try self.writeDir(root, .{});30 try w.writeDir(root, .{});
2731
28 self.prefix = root;32 w.prefix = root;
29}33}
3034
31/// Writes directory.35pub fn writeDir(w: *Writer, sub_path: []const u8, options: Options) Error!void {
32pub fn writeDir(self: *Self, sub_path: []const u8, opt: Options) !void {36 try w.writeHeader(.directory, sub_path, "", 0, options);
33 try self.writeHeader(.directory, sub_path, "", 0, opt);
34}37}
3538
36/// Writes file system file.39pub const WriteFileError = std.io.Writer.FileError || Error;
37pub fn writeFile(self: *Self, sub_path: []const u8, file: std.fs.File) !void {40
38 const stat = try file.stat();41pub fn writeFile(
42 w: *Writer,
43 sub_path: []const u8,
44 file: std.fs.File,
45 stat: std.fs.File.Stat,
46) WriteFileError!void {
39 const mtime: u64 = @intCast(@divFloor(stat.mtime, std.time.ns_per_s));47 const mtime: u64 = @intCast(@divFloor(stat.mtime, std.time.ns_per_s));
4048
41 var header = Header{};49 var header: Header = .{};
42 try self.setPath(&header, sub_path);50 try w.setPath(&header, sub_path);
43 try header.setSize(stat.size);51 try header.setSize(stat.size);
44 try header.setMtime(mtime);52 try header.setMtime(mtime);
45 try header.write(self.underlying_writer);53 try header.write(w.underlying_writer);
4654
47 try self.underlying_writer.writeFileAll(file, .{ .limit = .limited(stat.size) });55 try w.underlying_writer.writeFileAll(file, .{ .limit = .limited(stat.size) });
48 try self.writePadding(stat.size);56 try w.writePadding(stat.size);
49}57}
5058
51/// Writes file reading file content from `reader`. Number of bytes in59/// Writes file reading file content from `reader`. Reads exactly `size` bytes
52/// reader must be equal to `size`.60/// from `reader`, or returns `error.EndOfStream`.
53pub fn writeFileStream(self: *Self, sub_path: []const u8, size: usize, reader: anytype, opt: Options) !void {61pub fn writeFileStream(
54 try self.writeHeader(.regular, sub_path, "", @intCast(size), opt);62 w: *Writer,
5563 sub_path: []const u8,
56 var counting_reader = std.io.countingReader(reader);64 size: usize,
57 var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();65 reader: *std.io.BufferedReader,
58 try fifo.pump(counting_reader.reader(), self.underlying_writer);66 options: Options,
59 if (counting_reader.bytes_read != size) return error.WrongReaderSize;67) std.io.Reader.RwError!void {
60 try self.writePadding(size);68 try w.writeHeader(.regular, sub_path, "", @intCast(size), options);
69 try reader.readAll(w.underlying_writer, .limited(size));
70 try w.writePadding(size);
61}71}
6272
63/// Writes file using bytes buffer `content` for size and file content.73/// Writes file using bytes buffer `content` for size and file content.
64pub fn writeFileBytes(self: *Self, sub_path: []const u8, content: []const u8, opt: Options) !void {74pub fn writeFileBytes(w: *Writer, sub_path: []const u8, content: []const u8, options: Options) Error!void {
65 try self.writeHeader(.regular, sub_path, "", @intCast(content.len), opt);75 try w.writeHeader(.regular, sub_path, "", @intCast(content.len), options);
66 try self.underlying_writer.writeAll(content);76 try w.underlying_writer.writeAll(content);
67 try self.writePadding(content.len);77 try w.writePadding(content.len);
68}78}
6979
70/// Writes symlink.80pub fn writeLink(w: *Writer, sub_path: []const u8, link_name: []const u8, options: Options) Error!void {
71pub fn writeLink(self: *Self, sub_path: []const u8, link_name: []const u8, opt: Options) !void {81 try w.writeHeader(.symbolic_link, sub_path, link_name, 0, options);
72 try self.writeHeader(.symbolic_link, sub_path, link_name, 0, opt);
73}82}
7483
75/// Writes fs.Dir.WalkerEntry. Uses `mtime` from file system entry and84/// Writes fs.Dir.WalkerEntry. Uses `mtime` from file system entry and
76/// default for entry mode .85/// default for entry mode .
77pub fn writeEntry(self: *Self, entry: std.fs.Dir.Walker.Entry) !void {86pub fn writeEntry(w: *Writer, entry: std.fs.Dir.Walker.Entry) Error!void {
78 switch (entry.kind) {87 switch (entry.kind) {
79 .directory => {88 .directory => {
80 try self.writeDir(entry.path, .{ .mtime = try entryMtime(entry) });89 try w.writeDir(entry.path, .{ .mtime = try entryMtime(entry) });
81 },90 },
82 .file => {91 .file => {
83 var file = try entry.dir.openFile(entry.basename, .{});92 var file = try entry.dir.openFile(entry.basename, .{});
84 defer file.close();93 defer file.close();
85 try self.writeFile(entry.path, file);94 const stat = try file.stat();
95 try w.writeFile(entry.path, file, stat);
86 },96 },
87 .sym_link => {97 .sym_link => {
88 var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined;98 var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined;
89 const link_name = try entry.dir.readLink(entry.basename, &link_name_buffer);99 const link_name = try entry.dir.readLink(entry.basename, &link_name_buffer);
90 try self.writeLink(entry.path, link_name, .{ .mtime = try entryMtime(entry) });100 try w.writeLink(entry.path, link_name, .{ .mtime = try entryMtime(entry) });
91 },101 },
92 else => {102 else => {
93 return error.UnsupportedWalkerEntryKind;103 return error.UnsupportedWalkerEntryKind;
...@@ -96,31 +106,31 @@ pub fn writeEntry(self: *Self, entry: std.fs.Dir.Walker.Entry) !void {...@@ -96,31 +106,31 @@ pub fn writeEntry(self: *Self, entry: std.fs.Dir.Walker.Entry) !void {
96}106}
97107
98fn writeHeader(108fn writeHeader(
99 self: *Self,109 w: *Writer,
100 typeflag: Header.FileType,110 typeflag: Header.FileType,
101 sub_path: []const u8,111 sub_path: []const u8,
102 link_name: []const u8,112 link_name: []const u8,
103 size: u64,113 size: u64,
104 opt: Options,114 options: Options,
105) !void {115) Error!void {
106 var header = Header.init(typeflag);116 var header = Header.init(typeflag);
107 try self.setPath(&header, sub_path);117 try w.setPath(&header, sub_path);
108 try header.setSize(size);118 try header.setSize(size);
109 try header.setMtime(if (opt.mtime != 0) opt.mtime else self.mtimeNow());119 try header.setMtime(if (options.mtime != 0) options.mtime else w.mtimeNow());
110 if (opt.mode != 0)120 if (options.mode != 0)
111 try header.setMode(opt.mode);121 try header.setMode(options.mode);
112 if (typeflag == .symbolic_link)122 if (typeflag == .symbolic_link)
113 header.setLinkname(link_name) catch |err| switch (err) {123 header.setLinkname(link_name) catch |err| switch (err) {
114 error.NameTooLong => try self.writeExtendedHeader(.gnu_long_link, &.{link_name}),124 error.NameTooLong => try w.writeExtendedHeader(.gnu_long_link, &.{link_name}),
115 else => return err,125 else => return err,
116 };126 };
117 try header.write(self.underlying_writer);127 try header.write(w.underlying_writer);
118}128}
119129
120fn mtimeNow(self: *Self) u64 {130fn mtimeNow(w: *Writer) u64 {
121 if (self.mtime_now == 0)131 if (w.mtime_now == 0)
122 self.mtime_now = @intCast(std.time.timestamp());132 w.mtime_now = @intCast(std.time.timestamp());
123 return self.mtime_now;133 return w.mtime_now;
124}134}
125135
126fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 {136fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 {
...@@ -130,52 +140,51 @@ fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 {...@@ -130,52 +140,51 @@ fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 {
130140
131/// Writes path in posix header, if don't fit (in name+prefix; 100+155141/// Writes path in posix header, if don't fit (in name+prefix; 100+155
132/// bytes) writes it in gnu extended header.142/// bytes) writes it in gnu extended header.
133fn setPath(self: *Self, header: *Header, sub_path: []const u8) !void {143fn setPath(w: *Writer, header: *Header, sub_path: []const u8) Error!void {
134 header.setPath(self.prefix, sub_path) catch |err| switch (err) {144 header.setPath(w.prefix, sub_path) catch |err| switch (err) {
135 error.NameTooLong => {145 error.NameTooLong => {
136 // write extended header146 // write extended header
137 const buffers: []const []const u8 = if (self.prefix.len == 0)147 const buffers: []const []const u8 = if (w.prefix.len == 0)
138 &.{sub_path}148 &.{sub_path}
139 else149 else
140 &.{ self.prefix, "/", sub_path };150 &.{ w.prefix, "/", sub_path };
141 try self.writeExtendedHeader(.gnu_long_name, buffers);151 try w.writeExtendedHeader(.gnu_long_name, buffers);
142 },152 },
143 else => return err,153 else => return err,
144 };154 };
145}155}
146156
147/// Writes gnu extended header: gnu_long_name or gnu_long_link.157/// Writes gnu extended header: gnu_long_name or gnu_long_link.
148fn writeExtendedHeader(self: *Self, typeflag: Header.FileType, buffers: []const []const u8) !void {158fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const []const u8) Error!void {
149 var len: usize = 0;159 var len: usize = 0;
150 for (buffers) |buf|160 for (buffers) |buf| len += buf.len;
151 len += buf.len;
152161
153 var header = Header.init(typeflag);162 var header: Header = .init(typeflag);
154 try header.setSize(len);163 try header.setSize(len);
155 try header.write(self.underlying_writer);164 try header.write(w.underlying_writer);
156 for (buffers) |buf|165 for (buffers) |buf|
157 try self.underlying_writer.writeAll(buf);166 try w.underlying_writer.writeAll(buf);
158 try self.writePadding(len);167 try w.writePadding(len);
159}168}
160169
161fn writePadding(self: *Self, bytes: u64) !void {170fn writePadding(w: *Writer, bytes: usize) std.io.Writer.Error!void {
162 const pos: usize = @intCast(bytes % block_size);171 const pos = bytes % block_size;
163 if (pos == 0) return;172 if (pos == 0) return;
164 try self.underlying_writer.writeAll(empty_block[pos..]);173 try w.underlying_writer.splatByteAll(0, block_size - pos);
165}174}
166175
167/// Tar should finish with two zero blocks, but 'reasonable system must176/// According to the specification, tar should finish with two zero blocks, but
168/// not assume that such a block exists when reading an archive' (from177/// "reasonable system must not assume that such a block exists when reading an
169/// reference). In practice it is safe to skip this finish.178/// archive". Therefore, the Zig standard library recommends to not call this
170pub fn finish(self: *Self) !void {179/// function.
171 try self.underlying_writer.writeAll(&empty_block);180pub fn finishPedantically(w: *Writer) std.io.Writer.Error!void {
172 try self.underlying_writer.writeAll(&empty_block);181 try w.underlying_writer.writeSplatAll(&.{&.{0}}, block_size * 2);
173}182}
174183
175/// A struct that is exactly 512 bytes and matches tar file format. This is184/// A struct that is exactly 512 bytes and matches tar file format. This is
176/// intended to be used for outputting tar files; for parsing there is185/// intended to be used for outputting tar files; for parsing there is
177/// `std.tar.Header`.186/// `std.tar.Header`.
178const Header = extern struct {187pub const Header = extern struct {
179 // This struct was originally copied from188 // This struct was originally copied from
180 // https://github.com/mattnite/tar/blob/main/src/main.zig which is MIT189 // https://github.com/mattnite/tar/blob/main/src/main.zig which is MIT
181 // licensed.190 // licensed.
...@@ -230,11 +239,11 @@ const Header = extern struct {...@@ -230,11 +239,11 @@ const Header = extern struct {
230 };239 };
231 }240 }
232241
233 pub fn setSize(self: *Header, size: u64) !void {242 pub fn setSize(w: *Header, size: u64) error{OctalOverflow}!void {
234 try octal(&self.size, size);243 try octal(&w.size, size);
235 }244 }
236245
237 fn octal(buf: []u8, value: u64) !void {246 fn octal(buf: []u8, value: u64) error{OctalOverflow}!void {
238 var remainder: u64 = value;247 var remainder: u64 = value;
239 var pos: usize = buf.len;248 var pos: usize = buf.len;
240 while (remainder > 0 and pos > 0) {249 while (remainder > 0 and pos > 0) {
...@@ -246,36 +255,36 @@ const Header = extern struct {...@@ -246,36 +255,36 @@ const Header = extern struct {
246 }255 }
247 }256 }
248257
249 pub fn setMode(self: *Header, mode: u32) !void {258 pub fn setMode(w: *Header, mode: u32) error{OctalOverflow}!void {
250 try octal(&self.mode, mode);259 try octal(&w.mode, mode);
251 }260 }
252261
253 // Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time.262 // Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time.
254 // mtime == 0 will use current time263 // mtime == 0 will use current time
255 pub fn setMtime(self: *Header, mtime: u64) !void {264 pub fn setMtime(w: *Header, mtime: u64) error{OctalOverflow}!void {
256 try octal(&self.mtime, mtime);265 try octal(&w.mtime, mtime);
257 }266 }
258267
259 pub fn updateChecksum(self: *Header) !void {268 pub fn updateChecksum(w: *Header) !void {
260 var checksum: usize = ' '; // other 7 self.checksum bytes are initialized to ' '269 var checksum: usize = ' '; // other 7 w.checksum bytes are initialized to ' '
261 for (std.mem.asBytes(self)) |val|270 for (std.mem.asBytes(w)) |val|
262 checksum += val;271 checksum += val;
263 try octal(&self.checksum, checksum);272 try octal(&w.checksum, checksum);
264 }273 }
265274
266 pub fn write(self: *Header, output_writer: anytype) !void {275 pub fn write(h: *Header, bw: *std.io.BufferedWriter) error{ OctalOverflow, WriteFailed }!void {
267 try self.updateChecksum();276 try h.updateChecksum();
268 try output_writer.writeAll(std.mem.asBytes(self));277 try bw.writeAll(std.mem.asBytes(h));
269 }278 }
270279
271 pub fn setLinkname(self: *Header, link: []const u8) !void {280 pub fn setLinkname(w: *Header, link: []const u8) !void {
272 if (link.len > self.linkname.len) return error.NameTooLong;281 if (link.len > w.linkname.len) return error.NameTooLong;
273 @memcpy(self.linkname[0..link.len], link);282 @memcpy(w.linkname[0..link.len], link);
274 }283 }
275284
276 pub fn setPath(self: *Header, prefix: []const u8, sub_path: []const u8) !void {285 pub fn setPath(w: *Header, prefix: []const u8, sub_path: []const u8) !void {
277 const max_prefix = self.prefix.len;286 const max_prefix = w.prefix.len;
278 const max_name = self.name.len;287 const max_name = w.name.len;
279 const sep = std.fs.path.sep_posix;288 const sep = std.fs.path.sep_posix;
280289
281 if (prefix.len + sub_path.len > max_name + max_prefix or prefix.len > max_prefix)290 if (prefix.len + sub_path.len > max_name + max_prefix or prefix.len > max_prefix)
...@@ -283,32 +292,32 @@ const Header = extern struct {...@@ -283,32 +292,32 @@ const Header = extern struct {
283292
284 // both fit into name293 // both fit into name
285 if (prefix.len > 0 and prefix.len + sub_path.len < max_name) {294 if (prefix.len > 0 and prefix.len + sub_path.len < max_name) {
286 @memcpy(self.name[0..prefix.len], prefix);295 @memcpy(w.name[0..prefix.len], prefix);
287 self.name[prefix.len] = sep;296 w.name[prefix.len] = sep;
288 @memcpy(self.name[prefix.len + 1 ..][0..sub_path.len], sub_path);297 @memcpy(w.name[prefix.len + 1 ..][0..sub_path.len], sub_path);
289 return;298 return;
290 }299 }
291300
292 // sub_path fits into name301 // sub_path fits into name
293 // there is no prefix or prefix fits into prefix302 // there is no prefix or prefix fits into prefix
294 if (sub_path.len <= max_name) {303 if (sub_path.len <= max_name) {
295 @memcpy(self.name[0..sub_path.len], sub_path);304 @memcpy(w.name[0..sub_path.len], sub_path);
296 @memcpy(self.prefix[0..prefix.len], prefix);305 @memcpy(w.prefix[0..prefix.len], prefix);
297 return;306 return;
298 }307 }
299308
300 if (prefix.len > 0) {309 if (prefix.len > 0) {
301 @memcpy(self.prefix[0..prefix.len], prefix);310 @memcpy(w.prefix[0..prefix.len], prefix);
302 self.prefix[prefix.len] = sep;311 w.prefix[prefix.len] = sep;
303 }312 }
304 const prefix_pos = if (prefix.len > 0) prefix.len + 1 else 0;313 const prefix_pos = if (prefix.len > 0) prefix.len + 1 else 0;
305314
306 // add as much to prefix as you can, must split at /315 // add as much to prefix as you can, must split at /
307 const prefix_remaining = max_prefix - prefix_pos;316 const prefix_remaining = max_prefix - prefix_pos;
308 if (std.mem.lastIndexOf(u8, sub_path[0..@min(prefix_remaining, sub_path.len)], &.{'/'})) |sep_pos| {317 if (std.mem.lastIndexOf(u8, sub_path[0..@min(prefix_remaining, sub_path.len)], &.{'/'})) |sep_pos| {
309 @memcpy(self.prefix[prefix_pos..][0..sep_pos], sub_path[0..sep_pos]);318 @memcpy(w.prefix[prefix_pos..][0..sep_pos], sub_path[0..sep_pos]);
310 if ((sub_path.len - sep_pos - 1) > max_name) return error.NameTooLong;319 if ((sub_path.len - sep_pos - 1) > max_name) return error.NameTooLong;
311 @memcpy(self.name[0..][0 .. sub_path.len - sep_pos - 1], sub_path[sep_pos + 1 ..]);320 @memcpy(w.name[0..][0 .. sub_path.len - sep_pos - 1], sub_path[sep_pos + 1 ..]);
312 return;321 return;
313 }322 }
314323
lib/std/zig/Server.zig+3-3
...@@ -173,7 +173,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {...@@ -173,7 +173,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
173 .bytes_len = @intCast(bytes_len),173 .bytes_len = @intCast(bytes_len),
174 });174 });
175 try s.out.writeStructEndian(eb_hdr, .little);175 try s.out.writeStructEndian(eb_hdr, .little);
176 try s.out.writeArrayEndian(u32, error_bundle.extra, .little);176 try s.out.writeSliceEndian(u32, error_bundle.extra, .little);
177 try s.out.writeAll(error_bundle.string_bytes);177 try s.out.writeAll(error_bundle.string_bytes);
178 try s.out.flush();178 try s.out.flush();
179}179}
...@@ -198,8 +198,8 @@ pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {...@@ -198,8 +198,8 @@ pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
198 .bytes_len = @intCast(bytes_len),198 .bytes_len = @intCast(bytes_len),
199 });199 });
200 try s.out.writeStructEndian(header, .little);200 try s.out.writeStructEndian(header, .little);
201 try s.out.writeArrayEndian(u32, test_metadata.names, .little);201 try s.out.writeSliceEndian(u32, test_metadata.names, .little);
202 try s.out.writeArrayEndian(u32, test_metadata.expected_panic_msgs, .little);202 try s.out.writeSliceEndian(u32, test_metadata.expected_panic_msgs, .little);
203 try s.out.writeAll(test_metadata.string_bytes);203 try s.out.writeAll(test_metadata.string_bytes);
204 try s.out.flush();204 try s.out.flush();
205}205}