authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-23 15:19:50-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:30-07:00
log837f2bfc697c604b0d16d789b4ca2752f58b0ed5
tree12b1b4957144957d3840327b90cf409a6328e1e5
parentaf24e722fba2245d50473dc7a6fb7671851e07dc

hello world compiling again


11 files changed, 253 insertions(+), 159 deletions(-)

lib/compiler/test_runner.zig+4-6
...@@ -69,13 +69,11 @@ var stdout_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = un...@@ -69,13 +69,11 @@ var stdout_buffer: [std.heap.page_size_min]u8 align(std.heap.page_size_min) = un
6969
70fn mainServer() !void {70fn mainServer() !void {
71 @disableInstrumentation();71 @disableInstrumentation();
72 var stdin_reader = std.fs.File.stdin().reader();72 var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer);
73 var stdout_writer = std.fs.File.stdout().writer();73 var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
74 var stdin_buffered_reader = stdin_reader.interface().buffered(&stdin_buffer);
75 var stdout_buffered_writer = stdout_writer.interface().buffered(&stdout_buffer);
76 var server = try std.zig.Server.init(.{74 var server = try std.zig.Server.init(.{
77 .in = &stdin_buffered_reader,75 .in = &stdin_reader.interface,
78 .out = &stdout_buffered_writer,76 .out = &stdout_writer.interface,
79 .zig_version = builtin.zig_version_string,77 .zig_version = builtin.zig_version_string,
80 });78 });
8179
lib/std/compress/flate/Decompress.zig+53-76
...@@ -1,22 +1,3 @@...@@ -1,22 +1,3 @@
1//! Inflate decompresses deflate bit stream. Reads compressed data from reader
2//! provided in init. Decompressed data are stored in internal hist buffer and
3//! can be accesses iterable `next` or reader interface.
4//!
5//! Container defines header/footer wrapper around deflate bit stream. Can be
6//! gzip or zlib.
7//!
8//! Deflate bit stream consists of multiple blocks. Block can be one of three types:
9//! * stored, non compressed, max 64k in size
10//! * fixed, huffman codes are predefined
11//! * dynamic, huffman code tables are encoded at the block start
12//!
13//! `step` function runs decoder until internal `hist` buffer is full. Client than needs to read
14//! that data in order to proceed with decoding.
15//!
16//! Allocates 74.5K of internal buffers, most important are:
17//! * 64K for history (CircularBuffer)
18//! * ~10K huffman decoders (Literal and DistanceDecoder)
19
20const std = @import("../../std.zig");1const std = @import("../../std.zig");
21const flate = std.compress.flate;2const flate = std.compress.flate;
22const Container = flate.Container;3const Container = flate.Container;
...@@ -24,16 +5,16 @@ const Token = @import("Token.zig");...@@ -24,16 +5,16 @@ const Token = @import("Token.zig");
24const testing = std.testing;5const testing = std.testing;
25const Decompress = @This();6const Decompress = @This();
26const Writer = std.io.Writer;7const Writer = std.io.Writer;
8const Reader = std.io.Reader;
279
28input: *std.io.Reader,10input: *Reader,
29// Hashes, produces checksum, of uncompressed data for gzip/zlib footer.11interface: Reader,
12/// Hashes, produces checksum, of uncompressed data for gzip/zlib footer.
30hasher: Container.Hasher,13hasher: Container.Hasher,
3114
32// dynamic block huffman code decoders
33lit_dec: LiteralDecoder,15lit_dec: LiteralDecoder,
34dst_dec: DistanceDecoder,16dst_dec: DistanceDecoder,
3517
36// current read state
37final_block: bool,18final_block: bool,
38state: State,19state: State,
3920
...@@ -68,8 +49,16 @@ pub const Error = Container.Error || error{...@@ -68,8 +49,16 @@ pub const Error = Container.Error || error{
68 MissingEndOfBlockCode,49 MissingEndOfBlockCode,
69};50};
7051
71pub fn init(input: *std.io.Reader, container: Container) Decompress {52pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
72 return .{53 return .{
54 .interface = .{
55 // TODO populate discard so that when an amount is discarded that
56 // includes an entire frame, skip decoding that frame.
57 .vtable = &.{ .stream = stream },
58 .buffer = buffer,
59 .seek = 0,
60 .end = 0,
61 },
73 .input = input,62 .input = input,
74 .hasher = .init(container),63 .hasher = .init(container),
75 .lit_dec = .{},64 .lit_dec = .{},
...@@ -140,13 +129,9 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {...@@ -140,13 +129,9 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
140 return sym;129 return sym;
141}130}
142131
143pub fn read(132pub fn stream(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
144 context: ?*anyopaque,133 const d: *Decompress = @alignCast(@fieldParentPtr("interface", r));
145 bw: *Writer,134 return readInner(d, w, limit) catch |err| switch (err) {
146 limit: std.io.Limit,
147) std.io.Reader.StreamError!usize {
148 const d: *Decompress = @alignCast(@ptrCast(context));
149 return readInner(d, bw, limit) catch |err| switch (err) {
150 error.EndOfStream => return error.EndOfStream,135 error.EndOfStream => return error.EndOfStream,
151 error.WriteFailed => return error.WriteFailed,136 error.WriteFailed => return error.WriteFailed,
152 else => |e| {137 else => |e| {
...@@ -158,11 +143,7 @@ pub fn read(...@@ -158,11 +143,7 @@ pub fn read(
158 };143 };
159}144}
160145
161fn readInner(146fn readInner(d: *Decompress, w: *Writer, limit: std.io.Limit) (Error || Reader.StreamError)!usize {
162 d: *Decompress,
163 bw: *Writer,
164 limit: std.io.Limit,
165) (Error || error{ WriteFailed, EndOfStream })!usize {
166 const in = d.input;147 const in = d.input;
167 sw: switch (d.state) {148 sw: switch (d.state) {
168 .protocol_header => switch (d.hasher.container()) {149 .protocol_header => switch (d.hasher.container()) {
...@@ -266,76 +247,75 @@ fn readInner(...@@ -266,76 +247,75 @@ fn readInner(
266 }247 }
267 },248 },
268 .stored_block => |remaining_len| {249 .stored_block => |remaining_len| {
269 const out = try bw.writableSliceGreedyPreserving(flate.history_len, 1);250 const out = try w.writableSliceGreedyPreserving(flate.history_len, 1);
270 const limited_out = limit.min(.limited(remaining_len)).slice(out);251 const limited_out = limit.min(.limited(remaining_len)).slice(out);
271 const n = try d.input.readVec(bw, &.{limited_out});252 const n = try d.input.readVec(&.{limited_out});
272 if (remaining_len - n == 0) {253 if (remaining_len - n == 0) {
273 d.state = if (d.final_block) .protocol_footer else .block_header;254 d.state = if (d.final_block) .protocol_footer else .block_header;
274 } else {255 } else {
275 d.state = .{ .stored_block = remaining_len - n };256 d.state = .{ .stored_block = @intCast(remaining_len - n) };
276 }257 }
277 bw.advance(n);258 w.advance(n);
278 return n;259 return n;
279 },260 },
280 .fixed_block => {261 .fixed_block => {
281 const start = bw.count;262 const start = w.count;
282 while (@intFromEnum(limit) > bw.count - start) {263 while (@intFromEnum(limit) > w.count - start) {
283 const code = try d.readFixedCode();264 const code = try d.readFixedCode();
284 switch (code) {265 switch (code) {
285 0...255 => try bw.writeBytePreserving(flate.history_len, @intCast(code)),266 0...255 => try w.writeBytePreserving(flate.history_len, @intCast(code)),
286 256 => {267 256 => {
287 d.state = if (d.final_block) .protocol_footer else .block_header;268 d.state = if (d.final_block) .protocol_footer else .block_header;
288 return bw.count - start;269 return w.count - start;
289 },270 },
290 257...285 => {271 257...285 => {
291 // Handles fixed block non literal (length) code.272 // Handles fixed block non literal (length) code.
292 // Length code is followed by 5 bits of distance code.273 // Length code is followed by 5 bits of distance code.
293 const rebased_code = code - 257;274 const length = try d.decodeLength(@intCast(code - 257));
294 const length = try d.decodeLength(rebased_code);
295 const distance = try d.decodeDistance(try d.takeBitsReverseBuffered(u5));275 const distance = try d.decodeDistance(try d.takeBitsReverseBuffered(u5));
296 try writeMatch(bw, length, distance);276 try writeMatch(w, length, distance);
297 },277 },
298 else => return error.InvalidCode,278 else => return error.InvalidCode,
299 }279 }
300 }280 }
301 d.state = .fixed_block;281 d.state = .fixed_block;
302 return bw.count - start;282 return w.count - start;
303 },283 },
304 .dynamic_block => {284 .dynamic_block => {
305 // In larger archives most blocks are usually dynamic, so decompression285 // In larger archives most blocks are usually dynamic, so decompression
306 // performance depends on this logic.286 // performance depends on this logic.
307 const start = bw.count;287 const start = w.count;
308 while (@intFromEnum(limit) > bw.count - start) {288 while (@intFromEnum(limit) > w.count - start) {
309 const sym = try d.decodeSymbol(&d.lit_dec);289 const sym = try d.decodeSymbol(&d.lit_dec);
310290
311 switch (sym.kind) {291 switch (sym.kind) {
312 .literal => d.hist.write(sym.symbol),292 .literal => try w.writeBytePreserving(flate.history_len, sym.symbol),
313 .match => {293 .match => {
314 // Decode match backreference <length, distance>294 // Decode match backreference <length, distance>
315 const length = try d.decodeLength(sym.symbol);295 const length = try d.decodeLength(sym.symbol);
316 const dsm = try d.decodeSymbol(&d.dst_dec);296 const dsm = try d.decodeSymbol(&d.dst_dec);
317 const distance = try d.decodeDistance(dsm.symbol);297 const distance = try d.decodeDistance(dsm.symbol);
318 try writeMatch(bw, length, distance);298 try writeMatch(w, length, distance);
319 },299 },
320 .end_of_block => {300 .end_of_block => {
321 d.state = if (d.final_block) .protocol_footer else .block_header;301 d.state = if (d.final_block) .protocol_footer else .block_header;
322 return bw.count - start;302 return w.count - start;
323 },303 },
324 }304 }
325 }305 }
326 d.state = .dynamic_block;306 d.state = .dynamic_block;
327 return bw.count - start;307 return w.count - start;
328 },308 },
329 .protocol_footer => {309 .protocol_footer => {
330 d.alignBitsToByte();310 d.alignBitsToByte();
331 switch (d.hasher.container()) {311 switch (d.hasher) {
332 .gzip => |*gzip| {312 .gzip => |*gzip| {
333 if (try reader.read(u32) != gzip.final()) return error.WrongGzipChecksum;313 if (try in.takeInt(u32, .little) != gzip.crc.final()) return error.WrongGzipChecksum;
334 if (try reader.read(u32) != gzip.count) return error.WrongGzipSize;314 if (try in.takeInt(u32, .little) != gzip.count) return error.WrongGzipSize;
335 },315 },
336 .zlib => |*zlib| {316 .zlib => |*zlib| {
337 const chksum: u32 = @byteSwap(zlib.final());317 const chksum: u32 = @byteSwap(zlib.final());
338 if (try reader.read(u32) != chksum) return error.WrongZlibChecksum;318 if (try in.takeInt(u32, .big) != chksum) return error.WrongZlibChecksum;
339 },319 },
340 .raw => {},320 .raw => {},
341 }321 }
...@@ -355,15 +335,12 @@ fn writeMatch(bw: *Writer, length: u16, distance: u16) !void {...@@ -355,15 +335,12 @@ fn writeMatch(bw: *Writer, length: u16, distance: u16) !void {
355 @panic("TODO");335 @panic("TODO");
356}336}
357337
358pub fn reader(self: *Decompress, buffer: []u8) std.io.Reader {338fn takeBits(d: *Decompress, comptime T: type) !T {
359 return .{339 _ = d;
360 .context = self,340 @panic("TODO");
361 .vtable = &.{ .read = read },
362 .buffer = buffer,
363 };
364}341}
365342
366fn takeBits(d: *Decompress, comptime T: type) !T {343fn takeBitsReverseBuffered(d: *Decompress, comptime T: type) !T {
367 _ = d;344 _ = d;
368 @panic("TODO");345 @panic("TODO");
369}346}
...@@ -725,8 +702,8 @@ test "decompress" {...@@ -725,8 +702,8 @@ test "decompress" {
725 },702 },
726 };703 };
727 for (cases) |c| {704 for (cases) |c| {
728 var fb: std.io.Reader = .fixed(c.in);705 var fb: Reader = .fixed(c.in);
729 var aw: std.io.Writer.Allocating = .init(testing.allocator);706 var aw: Writer.Allocating = .init(testing.allocator);
730 defer aw.deinit();707 defer aw.deinit();
731708
732 var decompress: Decompress = .init(&fb, .raw);709 var decompress: Decompress = .init(&fb, .raw);
...@@ -784,8 +761,8 @@ test "gzip decompress" {...@@ -784,8 +761,8 @@ test "gzip decompress" {
784 },761 },
785 };762 };
786 for (cases) |c| {763 for (cases) |c| {
787 var fb: std.io.Reader = .fixed(c.in);764 var fb: Reader = .fixed(c.in);
788 var aw: std.io.Writer.Allocating = .init(testing.allocator);765 var aw: Writer.Allocating = .init(testing.allocator);
789 defer aw.deinit();766 defer aw.deinit();
790767
791 var decompress: Decompress = .init(&fb, .gzip);768 var decompress: Decompress = .init(&fb, .gzip);
...@@ -812,8 +789,8 @@ test "zlib decompress" {...@@ -812,8 +789,8 @@ test "zlib decompress" {
812 },789 },
813 };790 };
814 for (cases) |c| {791 for (cases) |c| {
815 var fb: std.io.Reader = .fixed(c.in);792 var fb: Reader = .fixed(c.in);
816 var aw: std.io.Writer.Allocating = .init(testing.allocator);793 var aw: Writer.Allocating = .init(testing.allocator);
817 defer aw.deinit();794 defer aw.deinit();
818795
819 var decompress: Decompress = .init(&fb, .zlib);796 var decompress: Decompress = .init(&fb, .zlib);
...@@ -872,8 +849,8 @@ test "fuzzing tests" {...@@ -872,8 +849,8 @@ test "fuzzing tests" {
872 };849 };
873850
874 inline for (cases, 0..) |c, case_no| {851 inline for (cases, 0..) |c, case_no| {
875 var in: std.io.Reader = .fixed(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));852 var in: Reader = .fixed(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));
876 var aw: std.io.Writer.Allocating = .init(testing.allocator);853 var aw: Writer.Allocating = .init(testing.allocator);
877 defer aw.deinit();854 defer aw.deinit();
878 errdefer std.debug.print("test case failed {}\n", .{case_no});855 errdefer std.debug.print("test case failed {}\n", .{case_no});
879856
...@@ -893,8 +870,8 @@ test "bug 18966" {...@@ -893,8 +870,8 @@ test "bug 18966" {
893 const input = @embedFile("testdata/fuzz/bug_18966.input");870 const input = @embedFile("testdata/fuzz/bug_18966.input");
894 const expect = @embedFile("testdata/fuzz/bug_18966.expect");871 const expect = @embedFile("testdata/fuzz/bug_18966.expect");
895872
896 var in: std.io.Reader = .fixed(input);873 var in: Reader = .fixed(input);
897 var aw: std.io.Writer.Allocating = .init(testing.allocator);874 var aw: Writer.Allocating = .init(testing.allocator);
898 defer aw.deinit();875 defer aw.deinit();
899876
900 var decompress: Decompress = .init(&in, .gzip);877 var decompress: Decompress = .init(&in, .gzip);
...@@ -909,7 +886,7 @@ test "reading into empty buffer" {...@@ -909,7 +886,7 @@ test "reading into empty buffer" {
909 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen886 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
910 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data887 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
911 };888 };
912 var in: std.io.Reader = .fixed(input);889 var in: Reader = .fixed(input);
913 var decomp: Decompress = .init(&in, .raw);890 var decomp: Decompress = .init(&in, .raw);
914 var decompress_br = decomp.readable(&.{});891 var decompress_br = decomp.readable(&.{});
915 var buf: [0]u8 = undefined;892 var buf: [0]u8 = undefined;
lib/std/crypto/Certificate/Bundle.zig+14-13
...@@ -200,10 +200,9 @@ pub fn addCertsFromFilePathAbsolute(...@@ -200,10 +200,9 @@ pub fn addCertsFromFilePathAbsolute(
200 gpa: Allocator,200 gpa: Allocator,
201 abs_file_path: []const u8,201 abs_file_path: []const u8,
202) AddCertsFromFilePathError!void {202) AddCertsFromFilePathError!void {
203 assert(fs.path.isAbsolute(abs_file_path));203 var file_reader: fs.File.Reader = .init(try fs.openFileAbsolute(abs_file_path, .{}), &.{});
204 var file = try fs.openFileAbsolute(abs_file_path, .{});204 defer file_reader.file.close();
205 defer file.close();205 return addCertsFromFile(cb, gpa, &file_reader, std.time.timestamp());
206 return addCertsFromFile(cb, gpa, file);
207}206}
208207
209pub fn addCertsFromFilePath(208pub fn addCertsFromFilePath(
...@@ -212,9 +211,9 @@ pub fn addCertsFromFilePath(...@@ -212,9 +211,9 @@ pub fn addCertsFromFilePath(
212 dir: fs.Dir,211 dir: fs.Dir,
213 sub_file_path: []const u8,212 sub_file_path: []const u8,
214) AddCertsFromFilePathError!void {213) AddCertsFromFilePathError!void {
215 var file = try dir.openFile(sub_file_path, .{});214 var file_reader: fs.File.Reader = .init(try dir.openFile(sub_file_path, .{}), &.{});
216 defer file.close();215 defer file_reader.file.close();
217 return addCertsFromFile(cb, gpa, file);216 return addCertsFromFile(cb, gpa, &file_reader, std.time.timestamp());
218}217}
219218
220pub const AddCertsFromFileError = Allocator.Error ||219pub const AddCertsFromFileError = Allocator.Error ||
...@@ -224,10 +223,14 @@ pub const AddCertsFromFileError = Allocator.Error ||...@@ -224,10 +223,14 @@ pub const AddCertsFromFileError = Allocator.Error ||
224 std.base64.Error ||223 std.base64.Error ||
225 error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker };224 error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker };
226225
227pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFromFileError!void {226/// `file_reader` needs no buffer since it will be read directly into `Bundle`.
228 var file_reader = file.reader();227pub fn addCertsFromFile(
228 cb: *Bundle,
229 gpa: Allocator,
230 file_reader: *fs.File.Reader,
231 now_sec: i64,
232) AddCertsFromFileError!void {
229 const size = try file_reader.getSize();233 const size = try file_reader.getSize();
230 var br = file_reader.interface().unbuffered();
231234
232 // We borrow `bytes` as a temporary buffer for the base64-encoded data.235 // We borrow `bytes` as a temporary buffer for the base64-encoded data.
233 // This is possible by computing the decoded length and reserving the space236 // This is possible by computing the decoded length and reserving the space
...@@ -238,7 +241,7 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom...@@ -238,7 +241,7 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom
238 try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);241 try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);
239 const end_reserved: u32 = @intCast(cb.bytes.items.len + decoded_size_upper_bound);242 const end_reserved: u32 = @intCast(cb.bytes.items.len + decoded_size_upper_bound);
240 const buffer = cb.bytes.allocatedSlice()[end_reserved..];243 const buffer = cb.bytes.allocatedSlice()[end_reserved..];
241 const end_index = br.readSliceShort(buffer) catch |err| switch (err) {244 const end_index = file_reader.interface.readSliceShort(buffer) catch |err| switch (err) {
242 error.ReadFailed => return file_reader.err.?,245 error.ReadFailed => return file_reader.err.?,
243 };246 };
244 const encoded_bytes = buffer[0..end_index];247 const encoded_bytes = buffer[0..end_index];
...@@ -246,8 +249,6 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom...@@ -246,8 +249,6 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom
246 const begin_marker = "-----BEGIN CERTIFICATE-----";249 const begin_marker = "-----BEGIN CERTIFICATE-----";
247 const end_marker = "-----END CERTIFICATE-----";250 const end_marker = "-----END CERTIFICATE-----";
248251
249 const now_sec = std.time.timestamp();
250
251 var start_index: usize = 0;252 var start_index: usize = 0;
252 while (mem.indexOfPos(u8, encoded_bytes, start_index, begin_marker)) |begin_marker_start| {253 while (mem.indexOfPos(u8, encoded_bytes, start_index, begin_marker)) |begin_marker_start| {
253 const cert_start = begin_marker_start + begin_marker.len;254 const cert_start = begin_marker_start + begin_marker.len;
lib/std/debug/Dwarf.zig+3-2
...@@ -2240,9 +2240,10 @@ pub const ElfModule = struct {...@@ -2240,9 +2240,10 @@ pub const ElfModule = struct {
2240 if (chdr.ch_type != .ZLIB) continue;2240 if (chdr.ch_type != .ZLIB) continue;
2241 const ch_size = chdr.ch_size;2241 const ch_size = chdr.ch_size;
22422242
2243 var zlib_stream: std.compress.flate.Decompress = .init(&section_reader, .zlib);2243 var zlib_stream: std.compress.flate.Decompress = .init(&section_reader, .zlib, &.{});
22442244
2245 const decompressed_section = zlib_stream.reader().readRemainingAlloc(gpa, .limited(ch_size)) catch continue;2245 const decompressed_section = zlib_stream.interface.allocRemaining(gpa, .limited(ch_size)) catch
2246 continue;
2246 if (decompressed_section.len != ch_size) {2247 if (decompressed_section.len != ch_size) {
2247 gpa.free(decompressed_section);2248 gpa.free(decompressed_section);
2248 continue;2249 continue;
lib/std/fmt.zig+12-12
...@@ -830,11 +830,11 @@ pub const BufPrintError = error{...@@ -830,11 +830,11 @@ pub const BufPrintError = error{
830830
831/// Print a Formatter string into `buf`. Returns a slice of the bytes printed.831/// Print a Formatter string into `buf`. Returns a slice of the bytes printed.
832pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {832pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
833 var bw: Writer = .fixed(buf);833 var w: Writer = .fixed(buf);
834 bw.print(fmt, args) catch |err| switch (err) {834 w.print(fmt, args) catch |err| switch (err) {
835 error.WriteFailed => return error.NoSpaceLeft,835 error.WriteFailed => return error.NoSpaceLeft,
836 };836 };
837 return bw.getWritten();837 return w.buffered();
838}838}
839839
840pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![:0]u8 {840pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![:0]u8 {
...@@ -1012,17 +1012,17 @@ test "int.padded" {...@@ -1012,17 +1012,17 @@ test "int.padded" {
1012test "buffer" {1012test "buffer" {
1013 {1013 {
1014 var buf1: [32]u8 = undefined;1014 var buf1: [32]u8 = undefined;
1015 var bw: Writer = .fixed(&buf1);1015 var w: Writer = .fixed(&buf1);
1016 try bw.printValue("", .{}, 1234, std.options.fmt_max_depth);1016 try w.printValue("", .{}, 1234, std.options.fmt_max_depth);
1017 try std.testing.expectEqualStrings("1234", bw.getWritten());1017 try std.testing.expectEqualStrings("1234", w.buffered());
10181018
1019 bw = .fixed(&buf1);1019 w = .fixed(&buf1);
1020 try bw.printValue("c", .{}, 'a', std.options.fmt_max_depth);1020 try w.printValue("c", .{}, 'a', std.options.fmt_max_depth);
1021 try std.testing.expectEqualStrings("a", bw.getWritten());1021 try std.testing.expectEqualStrings("a", w.buffered());
10221022
1023 bw = .fixed(&buf1);1023 w = .fixed(&buf1);
1024 try bw.printValue("b", .{}, 0b1100, std.options.fmt_max_depth);1024 try w.printValue("b", .{}, 0b1100, std.options.fmt_max_depth);
1025 try std.testing.expectEqualStrings("1100", bw.getWritten());1025 try std.testing.expectEqualStrings("1100", w.buffered());
1026 }1026 }
1027}1027}
10281028
lib/std/fs/AtomicFile.zig+3-5
...@@ -26,6 +26,7 @@ pub fn init(...@@ -26,6 +26,7 @@ pub fn init(
26 mode: File.Mode,26 mode: File.Mode,
27 dir: Dir,27 dir: Dir,
28 close_dir_on_deinit: bool,28 close_dir_on_deinit: bool,
29 write_buffer: []u8,
29) InitError!AtomicFile {30) InitError!AtomicFile {
30 var rand_buf: [random_bytes_len]u8 = undefined;31 var rand_buf: [random_bytes_len]u8 = undefined;
31 var tmp_path_buf: [tmp_path_len:0]u8 = undefined;32 var tmp_path_buf: [tmp_path_len:0]u8 = undefined;
...@@ -35,16 +36,13 @@ pub fn init(...@@ -35,16 +36,13 @@ pub fn init(
35 const tmp_path = fs.base64_encoder.encode(&tmp_path_buf, &rand_buf);36 const tmp_path = fs.base64_encoder.encode(&tmp_path_buf, &rand_buf);
36 tmp_path_buf[tmp_path.len] = 0;37 tmp_path_buf[tmp_path.len] = 0;
3738
38 const file = dir.createFile(39 const file = dir.createFile(tmp_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) {
39 tmp_path,
40 .{ .mode = mode, .exclusive = true },
41 ) catch |err| switch (err) {
42 error.PathAlreadyExists => continue,40 error.PathAlreadyExists => continue,
43 else => |e| return e,41 else => |e| return e,
44 };42 };
4543
46 return .{44 return .{
47 .file_writer = file.writer(),45 .file_writer = file.writer(write_buffer),
48 .tmp_path_buf = tmp_path_buf,46 .tmp_path_buf = tmp_path_buf,
49 .dest_basename = dest_basename,47 .dest_basename = dest_basename,
50 .file_open = true,48 .file_open = true,
lib/std/fs/Dir.zig+9-5
...@@ -2609,12 +2609,15 @@ pub fn updateFile(...@@ -2609,12 +2609,15 @@ pub fn updateFile(
2609 try dest_dir.makePath(dirname);2609 try dest_dir.makePath(dirname);
2610 }2610 }
26112611
2612 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });2612 var buffer: [2000]u8 = undefined;
2613 var atomic_file = try dest_dir.atomicFile(dest_path, .{
2614 .mode = actual_mode,
2615 .write_buffer = &buffer,
2616 });
2613 defer atomic_file.deinit();2617 defer atomic_file.deinit();
26142618
2615 var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size);2619 var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size);
2616 var buffer: [2000]u8 = undefined;2620 const dest_writer = &atomic_file.file_writer.interface;
2617 var dest_writer = atomic_file.file_writer.writer(&buffer);
26182621
2619 dest_writer.writeFileAll(&src_reader, .{}) catch |err| switch (err) {2622 dest_writer.writeFileAll(&src_reader, .{}) catch |err| switch (err) {
2620 error.ReadFailed => return src_reader.err.?,2623 error.ReadFailed => return src_reader.err.?,
...@@ -2715,6 +2718,7 @@ fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRa...@@ -2715,6 +2718,7 @@ fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRa
2715pub const AtomicFileOptions = struct {2718pub const AtomicFileOptions = struct {
2716 mode: File.Mode = File.default_mode,2719 mode: File.Mode = File.default_mode,
2717 make_path: bool = false,2720 make_path: bool = false,
2721 write_buffer: []u8,
2718};2722};
27192723
2720/// Directly access the `.file` field, and then call `AtomicFile.finish` to2724/// Directly access the `.file` field, and then call `AtomicFile.finish` to
...@@ -2732,9 +2736,9 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions)...@@ -2732,9 +2736,9 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions)
2732 else2736 else
2733 try self.openDir(dirname, .{});2737 try self.openDir(dirname, .{});
27342738
2735 return AtomicFile.init(fs.path.basename(dest_path), options.mode, dir, true);2739 return .init(fs.path.basename(dest_path), options.mode, dir, true, options.write_buffer);
2736 } else {2740 } else {
2737 return AtomicFile.init(dest_path, options.mode, self, false);2741 return .init(dest_path, options.mode, self, false, options.write_buffer);
2738 }2742 }
2739}2743}
27402744
lib/std/http/test.zig+6-12
...@@ -19,10 +19,8 @@ test "trailers" {...@@ -19,10 +19,8 @@ test "trailers" {
19 const connection = try net_server.accept();19 const connection = try net_server.accept();
20 defer connection.stream.close();20 defer connection.stream.close();
2121
22 var stream_reader = connection.stream.reader();22 var connection_br = connection.stream.reader(&recv_buffer);
23 var stream_writer = connection.stream.writer();23 var connection_bw = connection.stream.writer(&send_buffer);
24 var connection_br = stream_reader.interface().buffered(&recv_buffer);
25 var connection_bw = stream_writer.interface().buffered(&send_buffer);
26 var server = http.Server.init(&connection_br, &connection_bw);24 var server = http.Server.init(&connection_br, &connection_bw);
2725
28 try expectEqual(.ready, server.reader.state);26 try expectEqual(.ready, server.reader.state);
...@@ -104,10 +102,8 @@ test "HTTP server handles a chunked transfer coding request" {...@@ -104,10 +102,8 @@ test "HTTP server handles a chunked transfer coding request" {
104 const connection = try net_server.accept();102 const connection = try net_server.accept();
105 defer connection.stream.close();103 defer connection.stream.close();
106104
107 var stream_reader = connection.stream.reader();105 var connection_br = connection.stream.reader(&recv_buffer);
108 var stream_writer = connection.stream.writer();106 var connection_bw = connection.stream.writer(&send_buffer);
109 var connection_br = stream_reader.interface().buffered(&recv_buffer);
110 var connection_bw = stream_writer.interface().buffered(&send_buffer);
111 var server = http.Server.init(&connection_br, &connection_bw);107 var server = http.Server.init(&connection_br, &connection_bw);
112 var request = try server.receiveHead();108 var request = try server.receiveHead();
113109
...@@ -1163,10 +1159,8 @@ test "redirect to different connection" {...@@ -1163,10 +1159,8 @@ test "redirect to different connection" {
1163 global.other_port.?,1159 global.other_port.?,
1164 });1160 });
11651161
1166 var stream_reader = connection.stream.reader();1162 var connection_br = connection.stream.reader(&recv_buffer);
1167 var stream_writer = connection.stream.writer();1163 var connection_bw = connection.stream.writer(&send_buffer);
1168 var connection_br = stream_reader.interface().buffered(&recv_buffer);
1169 var connection_bw = stream_writer.interface().buffered(&send_buffer);
1170 var server = http.Server.init(&connection_br, &connection_bw);1164 var server = http.Server.init(&connection_br, &connection_bw);
1171 var request = try server.receiveHead();1165 var request = try server.receiveHead();
1172 try expectEqualStrings(request.head.target, "/help");1166 try expectEqualStrings(request.head.target, "/help");
lib/std/io/Reader.zig+11-11
...@@ -13,7 +13,7 @@ const Limit = std.io.Limit;...@@ -13,7 +13,7 @@ const Limit = std.io.Limit;
1313
14pub const Limited = @import("Reader/Limited.zig");14pub const Limited = @import("Reader/Limited.zig");
1515
16context: ?*anyopaque,16context: ?*anyopaque = null,
17vtable: *const VTable,17vtable: *const VTable,
18buffer: []u8,18buffer: []u8,
19/// Number of bytes which have been consumed from `buffer`.19/// Number of bytes which have been consumed from `buffer`.
...@@ -214,7 +214,7 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo...@@ -214,7 +214,7 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo
214pub fn allocRemaining(r: *Reader, gpa: Allocator, limit: Limit) LimitedAllocError![]u8 {214pub fn allocRemaining(r: *Reader, gpa: Allocator, limit: Limit) LimitedAllocError![]u8 {
215 var buffer: ArrayList(u8) = .empty;215 var buffer: ArrayList(u8) = .empty;
216 defer buffer.deinit(gpa);216 defer buffer.deinit(gpa);
217 try appendRemaining(r, gpa, null, &buffer, limit, 1);217 try appendRemaining(r, gpa, null, &buffer, limit);
218 return buffer.toOwnedSlice(gpa);218 return buffer.toOwnedSlice(gpa);
219}219}
220220
...@@ -237,13 +237,13 @@ pub fn appendRemaining(...@@ -237,13 +237,13 @@ pub fn appendRemaining(
237 limit: Limit,237 limit: Limit,
238) LimitedAllocError!void {238) LimitedAllocError!void {
239 const buffer = r.buffer;239 const buffer = r.buffer;
240 const buffered = buffer[r.seek..r.end];240 const buffer_contents = buffer[r.seek..r.end];
241 const copy_len = limit.minInt(buffered.len);241 const copy_len = limit.minInt(buffer_contents.len);
242 try list.ensureUnusedCapacity(gpa, copy_len);242 try list.ensureUnusedCapacity(gpa, copy_len);
243 @memcpy(list.unusedCapacitySlice()[0..copy_len], buffer[0..copy_len]);243 @memcpy(list.unusedCapacitySlice()[0..copy_len], buffer[0..copy_len]);
244 list.items.len += copy_len;244 list.items.len += copy_len;
245 r.seek += copy_len;245 r.seek += copy_len;
246 if (copy_len == buffered.len) {246 if (copy_len == buffer_contents.len) {
247 r.seek = 0;247 r.seek = 0;
248 r.end = 0;248 r.end = 0;
249 }249 }
...@@ -251,7 +251,7 @@ pub fn appendRemaining(...@@ -251,7 +251,7 @@ pub fn appendRemaining(
251 while (true) {251 while (true) {
252 try list.ensureUnusedCapacity(gpa, 1);252 try list.ensureUnusedCapacity(gpa, 1);
253 const dest = remaining.slice(list.unusedCapacitySlice());253 const dest = remaining.slice(list.unusedCapacitySlice());
254 const additional_buffer = if (@intFromEnum(remaining) == dest.len) buffer else &.{};254 const additional_buffer: []u8 = if (@intFromEnum(remaining) == dest.len) buffer else &.{};
255 const n = readVec(r, &.{ dest, additional_buffer }) catch |err| switch (err) {255 const n = readVec(r, &.{ dest, additional_buffer }) catch |err| switch (err) {
256 error.EndOfStream => break,256 error.EndOfStream => break,
257 error.ReadFailed => return error.ReadFailed,257 error.ReadFailed => return error.ReadFailed,
...@@ -276,7 +276,7 @@ pub fn appendRemaining(...@@ -276,7 +276,7 @@ pub fn appendRemaining(
276/// The reader's internal logical seek position moves forward in accordance276/// The reader's internal logical seek position moves forward in accordance
277/// with the number of bytes returned from this function.277/// with the number of bytes returned from this function.
278pub fn readVec(r: *Reader, data: []const []u8) Error!usize {278pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
279 return readVec(r, data, .unlimited);279 return readVecLimit(r, data, .unlimited);
280}280}
281281
282/// Equivalent to `readVec` but reads at most `limit` bytes.282/// Equivalent to `readVec` but reads at most `limit` bytes.
...@@ -290,9 +290,9 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {...@@ -290,9 +290,9 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
290 comptime assert(@intFromEnum(Limit.unlimited) == std.math.maxInt(usize));290 comptime assert(@intFromEnum(Limit.unlimited) == std.math.maxInt(usize));
291 var remaining = @intFromEnum(limit);291 var remaining = @intFromEnum(limit);
292 for (data, 0..) |buf, i| {292 for (data, 0..) |buf, i| {
293 const buffered = r.buffer[r.seek..r.end];293 const buffer_contents = r.buffer[r.seek..r.end];
294 const copy_len = @min(buffered.len, buf.len, remaining);294 const copy_len = @min(buffer_contents.len, buf.len, remaining);
295 @memcpy(buf[0..copy_len], buffered[0..copy_len]);295 @memcpy(buf[0..copy_len], buffer_contents[0..copy_len]);
296 r.seek += copy_len;296 r.seek += copy_len;
297 remaining -= copy_len;297 remaining -= copy_len;
298 if (remaining == 0) break;298 if (remaining == 0) break;
...@@ -354,7 +354,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {...@@ -354,7 +354,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
354 return @intFromEnum(limit) - remaining;354 return @intFromEnum(limit) - remaining;
355}355}
356356
357pub fn bufferContents(r: *Reader) []u8 {357pub fn buffered(r: *Reader) []u8 {
358 return r.buffer[r.seek..r.end];358 return r.buffer[r.seek..r.end];
359}359}
360360
lib/std/io/Writer.zig+137-16
...@@ -57,8 +57,9 @@ pub const VTable = struct {...@@ -57,8 +57,9 @@ pub const VTable = struct {
57 /// Copies contents from an open file to the logical sink. `buffer[0..end]`57 /// Copies contents from an open file to the logical sink. `buffer[0..end]`
58 /// is consumed first, followed by `limit` bytes from `file_reader`.58 /// is consumed first, followed by `limit` bytes from `file_reader`.
59 ///59 ///
60 /// Number of bytes actually written is returned, excluding bytes from60 /// Number of bytes logically written is returned. This excludes bytes from
61 /// `buffer`. Bytes from `buffer` are tracked by modifying `end`.61 /// `buffer` because they have already been logically written. Number of
62 /// bytes consumed from `buffer` are tracked by modifying `end`.
62 ///63 ///
63 /// Number of bytes returned may be zero, which does not necessarily mean64 /// Number of bytes returned may be zero, which does not necessarily mean
64 /// end-of-stream. A subsequent call may return nonzero, or signal end of65 /// end-of-stream. A subsequent call may return nonzero, or signal end of
...@@ -162,7 +163,11 @@ pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usiz...@@ -162,7 +163,11 @@ pub fn writeSplat(w: *Writer, data: []const []const u8, splat: usize) Error!usiz
162 assert(data.len > 0);163 assert(data.len > 0);
163 const buffer = w.buffer;164 const buffer = w.buffer;
164 const count = countSplat(0, data, splat);165 const count = countSplat(0, data, splat);
165 if (w.end + count > buffer.len) return w.vtable.drain(w, data, splat);166 if (w.end + count > buffer.len) {
167 const n = try w.vtable.drain(w, data, splat);
168 w.count += n;
169 return n;
170 }
166 w.count += count;171 w.count += count;
167 for (data) |bytes| {172 for (data) |bytes| {
168 @memcpy(buffer[w.end..][0..bytes.len], bytes);173 @memcpy(buffer[w.end..][0..bytes.len], bytes);
...@@ -213,6 +218,18 @@ pub fn flush(w: *Writer) Error!void {...@@ -213,6 +218,18 @@ pub fn flush(w: *Writer) Error!void {
213 assert(0 == try w.vtable.drain(w, &.{}, 0));218 assert(0 == try w.vtable.drain(w, &.{}, 0));
214}219}
215220
221/// Calls `VTable.drain` but hides the last `preserve_length` bytes from the
222/// implementation, keeping them buffered.
223pub fn drainLimited(w: *Writer, preserve_length: usize) Error!void {
224 const temp_end = w.end -| preserve_length;
225 const preserved = w.buffer[temp_end..w.end];
226 w.end = temp_end;
227 defer w.end += preserved.len;
228 assert(0 == try w.vtable.drain(w, &.{""}, 1));
229 assert(w.end <= temp_end + preserved.len);
230 @memmove(w.buffer[w.end..][0..preserved.len], preserved);
231}
232
216pub fn unusedCapacitySlice(w: *const Writer) []u8 {233pub fn unusedCapacitySlice(w: *const Writer) []u8 {
217 return w.buffer[w.end..];234 return w.buffer[w.end..];
218}235}
...@@ -246,13 +263,30 @@ pub fn writableSlice(w: *Writer, len: usize) Error![]u8 {...@@ -246,13 +263,30 @@ pub fn writableSlice(w: *Writer, len: usize) Error![]u8 {
246/// If `minimum_length` is zero, this is equivalent to `unusedCapacitySlice`.263/// If `minimum_length` is zero, this is equivalent to `unusedCapacitySlice`.
247pub fn writableSliceGreedy(w: *Writer, minimum_length: usize) Error![]u8 {264pub fn writableSliceGreedy(w: *Writer, minimum_length: usize) Error![]u8 {
248 assert(w.buffer.len >= minimum_length);265 assert(w.buffer.len >= minimum_length);
249 while (true) {266 while (w.buffer.len - w.end < minimum_length) {
250 const cap_slice = w.buffer[w.end..];
251 if (cap_slice.len >= minimum_length) {
252 @branchHint(.likely);
253 return cap_slice;
254 }
255 assert(0 == try w.vtable.drain(w, &.{""}, 1));267 assert(0 == try w.vtable.drain(w, &.{""}, 1));
268 } else {
269 @branchHint(.likely);
270 return w.buffer[w.end..];
271 }
272}
273
274/// Asserts the provided buffer has total capacity enough for `minimum_length`
275/// and `preserve_length` combined.
276///
277/// Does not `advance` the buffer end position.
278///
279/// When draining the buffer, ensures that at least `preserve_length` bytes
280/// remain buffered.
281///
282/// If `preserve_length` is zero, this is equivalent to `writableSliceGreedy`.
283pub fn writableSliceGreedyPreserving(w: *Writer, preserve_length: usize, minimum_length: usize) Error![]u8 {
284 assert(w.buffer.len >= preserve_length + minimum_length);
285 while (w.buffer.len - w.end < minimum_length) {
286 try drainLimited(w, preserve_length);
287 } else {
288 @branchHint(.likely);
289 return w.buffer[w.end..];
256 }290 }
257}291}
258292
...@@ -376,20 +410,56 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {...@@ -376,20 +410,56 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {
376 w.count += bytes.len;410 w.count += bytes.len;
377 return bytes.len;411 return bytes.len;
378 }412 }
379 return w.vtable.drain(w, &.{bytes}, 1);413 const n = try w.vtable.drain(w, &.{bytes}, 1);
414 w.count += n;
415 return n;
416}
417
418/// Asserts `buffer` capacity exceeds `preserve_length`.
419pub fn writePreserving(w: *Writer, preserve_length: usize, bytes: []const u8) Error!usize {
420 assert(preserve_length <= w.buffer.len);
421 if (w.end + bytes.len <= w.buffer.len) {
422 @branchHint(.likely);
423 @memcpy(w.buffer[w.end..][0..bytes.len], bytes);
424 w.end += bytes.len;
425 w.count += bytes.len;
426 return bytes.len;
427 }
428 const temp_end = w.end -| preserve_length;
429 const preserved = w.buffer[temp_end..w.end];
430 w.end = temp_end;
431 defer w.end += preserved.len;
432 const n = try w.vtable.drain(w, &.{bytes}, 1);
433 w.count += n;
434 assert(w.end <= temp_end + preserved.len);
435 @memmove(w.buffer[w.end..][0..preserved.len], preserved);
436 return n;
380}437}
381438
382/// Calls `write` as many times as necessary such that all of `bytes` are439/// Calls `drain` as many times as necessary such that all of `bytes` are
383/// transferred.440/// transferred.
384pub fn writeAll(w: *Writer, bytes: []const u8) Error!void {441pub fn writeAll(w: *Writer, bytes: []const u8) Error!void {
385 var index: usize = 0;442 var index: usize = 0;
386 while (index < bytes.len) index += try w.write(bytes[index..]);443 while (index < bytes.len) index += try w.write(bytes[index..]);
387}444}
388445
446/// Calls `drain` as many times as necessary such that all of `bytes` are
447/// transferred.
448///
449/// When draining the buffer, ensures that at least `preserve_length` bytes
450/// remain buffered.
451///
452/// Asserts `buffer` capacity exceeds `preserve_length`.
453pub fn writeAllPreserving(w: *Writer, preserve_length: usize, bytes: []const u8) Error!void {
454 var index: usize = 0;
455 while (index < bytes.len) index += try w.writePreserving(preserve_length, bytes[index..]);
456}
457
389pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void {458pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void {
390 try std.fmt.format(w, format, args);459 try std.fmt.format(w, format, args);
391}460}
392461
462/// Calls `drain` as many times as necessary such that `byte` is transferred.
393pub fn writeByte(w: *Writer, byte: u8) Error!void {463pub fn writeByte(w: *Writer, byte: u8) Error!void {
394 while (w.buffer.len - w.end == 0) {464 while (w.buffer.len - w.end == 0) {
395 const n = try w.vtable.drain(w, &.{&.{byte}}, 1);465 const n = try w.vtable.drain(w, &.{&.{byte}}, 1);
...@@ -405,6 +475,19 @@ pub fn writeByte(w: *Writer, byte: u8) Error!void {...@@ -405,6 +475,19 @@ pub fn writeByte(w: *Writer, byte: u8) Error!void {
405 }475 }
406}476}
407477
478/// When draining the buffer, ensures that at least `preserve_length` bytes
479/// remain buffered.
480pub fn writeBytePreserving(w: *Writer, preserve_length: usize, byte: u8) Error!void {
481 while (w.buffer.len - w.end == 0) {
482 try drainLimited(w, preserve_length);
483 } else {
484 @branchHint(.likely);
485 w.buffer[w.end] = byte;
486 w.end += 1;
487 w.count += 1;
488 }
489}
490
408/// Writes the same byte many times, performing the underlying write call as491/// Writes the same byte many times, performing the underlying write call as
409/// many times as necessary.492/// many times as necessary.
410pub fn splatByteAll(w: *Writer, byte: u8, n: usize) Error!void {493pub fn splatByteAll(w: *Writer, byte: u8, n: usize) Error!void {
...@@ -496,9 +579,43 @@ pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void {...@@ -496,9 +579,43 @@ pub fn writeSliceSwap(w: *Writer, Elem: type, slice: []const Elem) Error!void {
496/// See `sendFileReading` for an alternative that does not have579/// See `sendFileReading` for an alternative that does not have
497/// `error.Unimplemented` in the error set.580/// `error.Unimplemented` in the error set.
498pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {581pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
499 const end = w.end;582 return w.vtable.sendFile(w, file_reader, limit);
500 const n = try w.vtable.sendFile(w, file_reader, limit);583}
501 return n -| end;584
585/// Forwards a `sendFile` to a second `Writer` instance. `w` is only used for
586/// its buffer, but it has its `end` and `count` adjusted accordingly depending
587/// on how much was consumed.
588///
589/// Returns how many bytes from `file_reader` were consumed.
590pub fn sendFileTo(w: *Writer, other: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
591 const header = w.buffered();
592 const new_end = other.end + header.len;
593 if (new_end <= other.buffer.len) {
594 @memcpy(other.buffer[other.end..][0..header.len], header);
595 other.end = new_end;
596 w.end = 0;
597 return other.vtable.sendFile(other, file_reader, limit);
598 }
599 assert(header.len > 0);
600 var vec_buf: [2][]const u8 = .{ header, undefined };
601 var vec_i: usize = 1;
602 const buffered_contents = limit.slice(file_reader.buffered());
603 if (buffered_contents.len > 0) {
604 vec_buf[vec_i] = buffered_contents;
605 vec_i += 1;
606 }
607 const n = try other.vtable.drain(other, vec_buf[0..vec_i], 1);
608 other.count += n;
609 if (n < header.len) {
610 const remaining = w.buffer[n..w.end];
611 @memmove(w.buffer[0..remaining.len], remaining);
612 w.end = remaining.len;
613 return 0;
614 }
615 w.end = 0;
616 const tossed = n - header.len;
617 file_reader.interface.toss(tossed);
618 return tossed;
502}619}
503620
504/// Asserts nonzero buffer capacity.621/// Asserts nonzero buffer capacity.
...@@ -1696,8 +1813,12 @@ pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) F...@@ -1696,8 +1813,12 @@ pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) F
1696 }1813 }
1697}1814}
16981815
1699/// This function is used by `VTable.drain` function implementations to1816/// Removes the first `n` bytes from `buffer` by shifting buffer contents,
1700/// implement partial drains.1817/// returning how many bytes are left after consuming the entire buffer, or
1818/// zero if the entire buffer was not consumed.
1819///
1820/// Useful for `VTable.drain` function implementations to implement partial
1821/// drains.
1701pub fn consume(w: *Writer, n: usize) usize {1822pub fn consume(w: *Writer, n: usize) usize {
1702 if (n < w.end) {1823 if (n < w.end) {
1703 const remaining = w.buffer[n..w.end];1824 const remaining = w.buffer[n..w.end];
lib/std/net.zig+1-1
...@@ -2093,7 +2093,7 @@ pub const Stream = struct {...@@ -2093,7 +2093,7 @@ pub const Stream = struct {
2093 },2093 },
2094 .buffer = buffer,2094 .buffer = buffer,
2095 },2095 },
2096 .file_writer = .initMode(stream.handle, &.{}, .streaming),2096 .file_writer = .initMode(.{ .handle = stream.handle }, &.{}, .streaming),
2097 };2097 };
2098 }2098 }
20992099