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
6969
7070fn mainServer() !void {
7171 @disableInstrumentation();
72 var stdin_reader = std.fs.File.stdin().reader();
73 var stdout_writer = std.fs.File.stdout().writer();
74 var stdin_buffered_reader = stdin_reader.interface().buffered(&stdin_buffer);
75 var stdout_buffered_writer = stdout_writer.interface().buffered(&stdout_buffer);
72 var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer);
73 var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
7674 var server = try std.zig.Server.init(.{
77 .in = &stdin_buffered_reader,
78 .out = &stdout_buffered_writer,
75 .in = &stdin_reader.interface,
76 .out = &stdout_writer.interface,
7977 .zig_version = builtin.zig_version_string,
8078 });
8179
lib/std/compress/flate/Decompress.zig+53-76
......@@ -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
201const std = @import("../../std.zig");
212const flate = std.compress.flate;
223const Container = flate.Container;
......@@ -24,16 +5,16 @@ const Token = @import("Token.zig");
245const testing = std.testing;
256const Decompress = @This();
267const Writer = std.io.Writer;
8const Reader = std.io.Reader;
279
28input: *std.io.Reader,
29// Hashes, produces checksum, of uncompressed data for gzip/zlib footer.
10input: *Reader,
11interface: Reader,
12/// Hashes, produces checksum, of uncompressed data for gzip/zlib footer.
3013hasher: Container.Hasher,
3114
32// dynamic block huffman code decoders
3315lit_dec: LiteralDecoder,
3416dst_dec: DistanceDecoder,
3517
36// current read state
3718final_block: bool,
3819state: State,
3920
......@@ -68,8 +49,16 @@ pub const Error = Container.Error || error{
6849 MissingEndOfBlockCode,
6950};
7051
71pub fn init(input: *std.io.Reader, container: Container) Decompress {
52pub fn init(input: *Reader, container: Container, buffer: []u8) Decompress {
7253 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 },
7362 .input = input,
7463 .hasher = .init(container),
7564 .lit_dec = .{},
......@@ -140,13 +129,9 @@ fn decodeSymbol(self: *Decompress, decoder: anytype) !Symbol {
140129 return sym;
141130}
142131
143pub fn read(
144 context: ?*anyopaque,
145 bw: *Writer,
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) {
132pub fn stream(r: *Reader, w: *Writer, limit: std.io.Limit) Reader.StreamError!usize {
133 const d: *Decompress = @alignCast(@fieldParentPtr("interface", r));
134 return readInner(d, w, limit) catch |err| switch (err) {
150135 error.EndOfStream => return error.EndOfStream,
151136 error.WriteFailed => return error.WriteFailed,
152137 else => |e| {
......@@ -158,11 +143,7 @@ pub fn read(
158143 };
159144}
160145
161fn readInner(
162 d: *Decompress,
163 bw: *Writer,
164 limit: std.io.Limit,
165) (Error || error{ WriteFailed, EndOfStream })!usize {
146fn readInner(d: *Decompress, w: *Writer, limit: std.io.Limit) (Error || Reader.StreamError)!usize {
166147 const in = d.input;
167148 sw: switch (d.state) {
168149 .protocol_header => switch (d.hasher.container()) {
......@@ -266,76 +247,75 @@ fn readInner(
266247 }
267248 },
268249 .stored_block => |remaining_len| {
269 const out = try bw.writableSliceGreedyPreserving(flate.history_len, 1);
250 const out = try w.writableSliceGreedyPreserving(flate.history_len, 1);
270251 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});
272253 if (remaining_len - n == 0) {
273254 d.state = if (d.final_block) .protocol_footer else .block_header;
274255 } else {
275 d.state = .{ .stored_block = remaining_len - n };
256 d.state = .{ .stored_block = @intCast(remaining_len - n) };
276257 }
277 bw.advance(n);
258 w.advance(n);
278259 return n;
279260 },
280261 .fixed_block => {
281 const start = bw.count;
282 while (@intFromEnum(limit) > bw.count - start) {
262 const start = w.count;
263 while (@intFromEnum(limit) > w.count - start) {
283264 const code = try d.readFixedCode();
284265 switch (code) {
285 0...255 => try bw.writeBytePreserving(flate.history_len, @intCast(code)),
266 0...255 => try w.writeBytePreserving(flate.history_len, @intCast(code)),
286267 256 => {
287268 d.state = if (d.final_block) .protocol_footer else .block_header;
288 return bw.count - start;
269 return w.count - start;
289270 },
290271 257...285 => {
291272 // Handles fixed block non literal (length) code.
292273 // Length code is followed by 5 bits of distance code.
293 const rebased_code = code - 257;
294 const length = try d.decodeLength(rebased_code);
274 const length = try d.decodeLength(@intCast(code - 257));
295275 const distance = try d.decodeDistance(try d.takeBitsReverseBuffered(u5));
296 try writeMatch(bw, length, distance);
276 try writeMatch(w, length, distance);
297277 },
298278 else => return error.InvalidCode,
299279 }
300280 }
301281 d.state = .fixed_block;
302 return bw.count - start;
282 return w.count - start;
303283 },
304284 .dynamic_block => {
305285 // In larger archives most blocks are usually dynamic, so decompression
306286 // performance depends on this logic.
307 const start = bw.count;
308 while (@intFromEnum(limit) > bw.count - start) {
287 const start = w.count;
288 while (@intFromEnum(limit) > w.count - start) {
309289 const sym = try d.decodeSymbol(&d.lit_dec);
310290
311291 switch (sym.kind) {
312 .literal => d.hist.write(sym.symbol),
292 .literal => try w.writeBytePreserving(flate.history_len, sym.symbol),
313293 .match => {
314294 // Decode match backreference <length, distance>
315295 const length = try d.decodeLength(sym.symbol);
316296 const dsm = try d.decodeSymbol(&d.dst_dec);
317297 const distance = try d.decodeDistance(dsm.symbol);
318 try writeMatch(bw, length, distance);
298 try writeMatch(w, length, distance);
319299 },
320300 .end_of_block => {
321301 d.state = if (d.final_block) .protocol_footer else .block_header;
322 return bw.count - start;
302 return w.count - start;
323303 },
324304 }
325305 }
326306 d.state = .dynamic_block;
327 return bw.count - start;
307 return w.count - start;
328308 },
329309 .protocol_footer => {
330310 d.alignBitsToByte();
331 switch (d.hasher.container()) {
311 switch (d.hasher) {
332312 .gzip => |*gzip| {
333 if (try reader.read(u32) != gzip.final()) return error.WrongGzipChecksum;
334 if (try reader.read(u32) != gzip.count) return error.WrongGzipSize;
313 if (try in.takeInt(u32, .little) != gzip.crc.final()) return error.WrongGzipChecksum;
314 if (try in.takeInt(u32, .little) != gzip.count) return error.WrongGzipSize;
335315 },
336316 .zlib => |*zlib| {
337317 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;
339319 },
340320 .raw => {},
341321 }
......@@ -355,15 +335,12 @@ fn writeMatch(bw: *Writer, length: u16, distance: u16) !void {
355335 @panic("TODO");
356336}
357337
358pub fn reader(self: *Decompress, buffer: []u8) std.io.Reader {
359 return .{
360 .context = self,
361 .vtable = &.{ .read = read },
362 .buffer = buffer,
363 };
338fn takeBits(d: *Decompress, comptime T: type) !T {
339 _ = d;
340 @panic("TODO");
364341}
365342
366fn takeBits(d: *Decompress, comptime T: type) !T {
343fn takeBitsReverseBuffered(d: *Decompress, comptime T: type) !T {
367344 _ = d;
368345 @panic("TODO");
369346}
......@@ -725,8 +702,8 @@ test "decompress" {
725702 },
726703 };
727704 for (cases) |c| {
728 var fb: std.io.Reader = .fixed(c.in);
729 var aw: std.io.Writer.Allocating = .init(testing.allocator);
705 var fb: Reader = .fixed(c.in);
706 var aw: Writer.Allocating = .init(testing.allocator);
730707 defer aw.deinit();
731708
732709 var decompress: Decompress = .init(&fb, .raw);
......@@ -784,8 +761,8 @@ test "gzip decompress" {
784761 },
785762 };
786763 for (cases) |c| {
787 var fb: std.io.Reader = .fixed(c.in);
788 var aw: std.io.Writer.Allocating = .init(testing.allocator);
764 var fb: Reader = .fixed(c.in);
765 var aw: Writer.Allocating = .init(testing.allocator);
789766 defer aw.deinit();
790767
791768 var decompress: Decompress = .init(&fb, .gzip);
......@@ -812,8 +789,8 @@ test "zlib decompress" {
812789 },
813790 };
814791 for (cases) |c| {
815 var fb: std.io.Reader = .fixed(c.in);
816 var aw: std.io.Writer.Allocating = .init(testing.allocator);
792 var fb: Reader = .fixed(c.in);
793 var aw: Writer.Allocating = .init(testing.allocator);
817794 defer aw.deinit();
818795
819796 var decompress: Decompress = .init(&fb, .zlib);
......@@ -872,8 +849,8 @@ test "fuzzing tests" {
872849 };
873850
874851 inline for (cases, 0..) |c, case_no| {
875 var in: std.io.Reader = .fixed(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));
876 var aw: std.io.Writer.Allocating = .init(testing.allocator);
852 var in: Reader = .fixed(@embedFile("testdata/fuzz/" ++ c.input ++ ".input"));
853 var aw: Writer.Allocating = .init(testing.allocator);
877854 defer aw.deinit();
878855 errdefer std.debug.print("test case failed {}\n", .{case_no});
879856
......@@ -893,8 +870,8 @@ test "bug 18966" {
893870 const input = @embedFile("testdata/fuzz/bug_18966.input");
894871 const expect = @embedFile("testdata/fuzz/bug_18966.expect");
895872
896 var in: std.io.Reader = .fixed(input);
897 var aw: std.io.Writer.Allocating = .init(testing.allocator);
873 var in: Reader = .fixed(input);
874 var aw: Writer.Allocating = .init(testing.allocator);
898875 defer aw.deinit();
899876
900877 var decompress: Decompress = .init(&in, .gzip);
......@@ -909,7 +886,7 @@ test "reading into empty buffer" {
909886 0b0000_0001, 0b0000_1100, 0x00, 0b1111_0011, 0xff, // deflate fixed buffer header len, nlen
910887 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0x0a, // non compressed data
911888 };
912 var in: std.io.Reader = .fixed(input);
889 var in: Reader = .fixed(input);
913890 var decomp: Decompress = .init(&in, .raw);
914891 var decompress_br = decomp.readable(&.{});
915892 var buf: [0]u8 = undefined;
lib/std/crypto/Certificate/Bundle.zig+14-13
......@@ -200,10 +200,9 @@ pub fn addCertsFromFilePathAbsolute(
200200 gpa: Allocator,
201201 abs_file_path: []const u8,
202202) AddCertsFromFilePathError!void {
203 assert(fs.path.isAbsolute(abs_file_path));
204 var file = try fs.openFileAbsolute(abs_file_path, .{});
205 defer file.close();
206 return addCertsFromFile(cb, gpa, file);
203 var file_reader: fs.File.Reader = .init(try fs.openFileAbsolute(abs_file_path, .{}), &.{});
204 defer file_reader.file.close();
205 return addCertsFromFile(cb, gpa, &file_reader, std.time.timestamp());
207206}
208207
209208pub fn addCertsFromFilePath(
......@@ -212,9 +211,9 @@ pub fn addCertsFromFilePath(
212211 dir: fs.Dir,
213212 sub_file_path: []const u8,
214213) AddCertsFromFilePathError!void {
215 var file = try dir.openFile(sub_file_path, .{});
216 defer file.close();
217 return addCertsFromFile(cb, gpa, file);
214 var file_reader: fs.File.Reader = .init(try dir.openFile(sub_file_path, .{}), &.{});
215 defer file_reader.file.close();
216 return addCertsFromFile(cb, gpa, &file_reader, std.time.timestamp());
218217}
219218
220219pub const AddCertsFromFileError = Allocator.Error ||
......@@ -224,10 +223,14 @@ pub const AddCertsFromFileError = Allocator.Error ||
224223 std.base64.Error ||
225224 error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker };
226225
227pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFromFileError!void {
228 var file_reader = file.reader();
226/// `file_reader` needs no buffer since it will be read directly into `Bundle`.
227pub fn addCertsFromFile(
228 cb: *Bundle,
229 gpa: Allocator,
230 file_reader: *fs.File.Reader,
231 now_sec: i64,
232) AddCertsFromFileError!void {
229233 const size = try file_reader.getSize();
230 var br = file_reader.interface().unbuffered();
231234
232235 // We borrow `bytes` as a temporary buffer for the base64-encoded data.
233236 // 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
238241 try cb.bytes.ensureUnusedCapacity(gpa, needed_capacity);
239242 const end_reserved: u32 = @intCast(cb.bytes.items.len + decoded_size_upper_bound);
240243 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) {
242245 error.ReadFailed => return file_reader.err.?,
243246 };
244247 const encoded_bytes = buffer[0..end_index];
......@@ -246,8 +249,6 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFrom
246249 const begin_marker = "-----BEGIN CERTIFICATE-----";
247250 const end_marker = "-----END CERTIFICATE-----";
248251
249 const now_sec = std.time.timestamp();
250
251252 var start_index: usize = 0;
252253 while (mem.indexOfPos(u8, encoded_bytes, start_index, begin_marker)) |begin_marker_start| {
253254 const cert_start = begin_marker_start + begin_marker.len;
lib/std/debug/Dwarf.zig+3-2
......@@ -2240,9 +2240,10 @@ pub const ElfModule = struct {
22402240 if (chdr.ch_type != .ZLIB) continue;
22412241 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;
22462247 if (decompressed_section.len != ch_size) {
22472248 gpa.free(decompressed_section);
22482249 continue;
lib/std/fmt.zig+12-12
......@@ -830,11 +830,11 @@ pub const BufPrintError = error{
830830
831831/// Print a Formatter string into `buf`. Returns a slice of the bytes printed.
832832pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![]u8 {
833 var bw: Writer = .fixed(buf);
834 bw.print(fmt, args) catch |err| switch (err) {
833 var w: Writer = .fixed(buf);
834 w.print(fmt, args) catch |err| switch (err) {
835835 error.WriteFailed => return error.NoSpaceLeft,
836836 };
837 return bw.getWritten();
837 return w.buffered();
838838}
839839
840840pub fn bufPrintZ(buf: []u8, comptime fmt: []const u8, args: anytype) BufPrintError![:0]u8 {
......@@ -1012,17 +1012,17 @@ test "int.padded" {
10121012test "buffer" {
10131013 {
10141014 var buf1: [32]u8 = undefined;
1015 var bw: Writer = .fixed(&buf1);
1016 try bw.printValue("", .{}, 1234, std.options.fmt_max_depth);
1017 try std.testing.expectEqualStrings("1234", bw.getWritten());
1015 var w: Writer = .fixed(&buf1);
1016 try w.printValue("", .{}, 1234, std.options.fmt_max_depth);
1017 try std.testing.expectEqualStrings("1234", w.buffered());
10181018
1019 bw = .fixed(&buf1);
1020 try bw.printValue("c", .{}, 'a', std.options.fmt_max_depth);
1021 try std.testing.expectEqualStrings("a", bw.getWritten());
1019 w = .fixed(&buf1);
1020 try w.printValue("c", .{}, 'a', std.options.fmt_max_depth);
1021 try std.testing.expectEqualStrings("a", w.buffered());
10221022
1023 bw = .fixed(&buf1);
1024 try bw.printValue("b", .{}, 0b1100, std.options.fmt_max_depth);
1025 try std.testing.expectEqualStrings("1100", bw.getWritten());
1023 w = .fixed(&buf1);
1024 try w.printValue("b", .{}, 0b1100, std.options.fmt_max_depth);
1025 try std.testing.expectEqualStrings("1100", w.buffered());
10261026 }
10271027}
10281028
lib/std/fs/AtomicFile.zig+3-5
......@@ -26,6 +26,7 @@ pub fn init(
2626 mode: File.Mode,
2727 dir: Dir,
2828 close_dir_on_deinit: bool,
29 write_buffer: []u8,
2930) InitError!AtomicFile {
3031 var rand_buf: [random_bytes_len]u8 = undefined;
3132 var tmp_path_buf: [tmp_path_len:0]u8 = undefined;
......@@ -35,16 +36,13 @@ pub fn init(
3536 const tmp_path = fs.base64_encoder.encode(&tmp_path_buf, &rand_buf);
3637 tmp_path_buf[tmp_path.len] = 0;
3738
38 const file = dir.createFile(
39 tmp_path,
40 .{ .mode = mode, .exclusive = true },
41 ) catch |err| switch (err) {
39 const file = dir.createFile(tmp_path, .{ .mode = mode, .exclusive = true }) catch |err| switch (err) {
4240 error.PathAlreadyExists => continue,
4341 else => |e| return e,
4442 };
4543
4644 return .{
47 .file_writer = file.writer(),
45 .file_writer = file.writer(write_buffer),
4846 .tmp_path_buf = tmp_path_buf,
4947 .dest_basename = dest_basename,
5048 .file_open = true,
lib/std/fs/Dir.zig+9-5
......@@ -2609,12 +2609,15 @@ pub fn updateFile(
26092609 try dest_dir.makePath(dirname);
26102610 }
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 });
26132617 defer atomic_file.deinit();
26142618
26152619 var src_reader: File.Reader = .initSize(src_file, &.{}, src_stat.size);
2616 var buffer: [2000]u8 = undefined;
2617 var dest_writer = atomic_file.file_writer.writer(&buffer);
2620 const dest_writer = &atomic_file.file_writer.interface;
26182621
26192622 dest_writer.writeFileAll(&src_reader, .{}) catch |err| switch (err) {
26202623 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
27152718pub const AtomicFileOptions = struct {
27162719 mode: File.Mode = File.default_mode,
27172720 make_path: bool = false,
2721 write_buffer: []u8,
27182722};
27192723
27202724/// 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)
27322736 else
27332737 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);
27362740 } else {
2737 return AtomicFile.init(dest_path, options.mode, self, false);
2741 return .init(dest_path, options.mode, self, false, options.write_buffer);
27382742 }
27392743}
27402744
lib/std/http/test.zig+6-12
......@@ -19,10 +19,8 @@ test "trailers" {
1919 const connection = try net_server.accept();
2020 defer connection.stream.close();
2121
22 var stream_reader = connection.stream.reader();
23 var stream_writer = connection.stream.writer();
24 var connection_br = stream_reader.interface().buffered(&recv_buffer);
25 var connection_bw = stream_writer.interface().buffered(&send_buffer);
22 var connection_br = connection.stream.reader(&recv_buffer);
23 var connection_bw = connection.stream.writer(&send_buffer);
2624 var server = http.Server.init(&connection_br, &connection_bw);
2725
2826 try expectEqual(.ready, server.reader.state);
......@@ -104,10 +102,8 @@ test "HTTP server handles a chunked transfer coding request" {
104102 const connection = try net_server.accept();
105103 defer connection.stream.close();
106104
107 var stream_reader = connection.stream.reader();
108 var stream_writer = connection.stream.writer();
109 var connection_br = stream_reader.interface().buffered(&recv_buffer);
110 var connection_bw = stream_writer.interface().buffered(&send_buffer);
105 var connection_br = connection.stream.reader(&recv_buffer);
106 var connection_bw = connection.stream.writer(&send_buffer);
111107 var server = http.Server.init(&connection_br, &connection_bw);
112108 var request = try server.receiveHead();
113109
......@@ -1163,10 +1159,8 @@ test "redirect to different connection" {
11631159 global.other_port.?,
11641160 });
11651161
1166 var stream_reader = connection.stream.reader();
1167 var stream_writer = connection.stream.writer();
1168 var connection_br = stream_reader.interface().buffered(&recv_buffer);
1169 var connection_bw = stream_writer.interface().buffered(&send_buffer);
1162 var connection_br = connection.stream.reader(&recv_buffer);
1163 var connection_bw = connection.stream.writer(&send_buffer);
11701164 var server = http.Server.init(&connection_br, &connection_bw);
11711165 var request = try server.receiveHead();
11721166 try expectEqualStrings(request.head.target, "/help");
lib/std/io/Reader.zig+11-11
......@@ -13,7 +13,7 @@ const Limit = std.io.Limit;
1313
1414pub const Limited = @import("Reader/Limited.zig");
1515
16context: ?*anyopaque,
16context: ?*anyopaque = null,
1717vtable: *const VTable,
1818buffer: []u8,
1919/// Number of bytes which have been consumed from `buffer`.
......@@ -214,7 +214,7 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo
214214pub fn allocRemaining(r: *Reader, gpa: Allocator, limit: Limit) LimitedAllocError![]u8 {
215215 var buffer: ArrayList(u8) = .empty;
216216 defer buffer.deinit(gpa);
217 try appendRemaining(r, gpa, null, &buffer, limit, 1);
217 try appendRemaining(r, gpa, null, &buffer, limit);
218218 return buffer.toOwnedSlice(gpa);
219219}
220220
......@@ -237,13 +237,13 @@ pub fn appendRemaining(
237237 limit: Limit,
238238) LimitedAllocError!void {
239239 const buffer = r.buffer;
240 const buffered = buffer[r.seek..r.end];
241 const copy_len = limit.minInt(buffered.len);
240 const buffer_contents = buffer[r.seek..r.end];
241 const copy_len = limit.minInt(buffer_contents.len);
242242 try list.ensureUnusedCapacity(gpa, copy_len);
243243 @memcpy(list.unusedCapacitySlice()[0..copy_len], buffer[0..copy_len]);
244244 list.items.len += copy_len;
245245 r.seek += copy_len;
246 if (copy_len == buffered.len) {
246 if (copy_len == buffer_contents.len) {
247247 r.seek = 0;
248248 r.end = 0;
249249 }
......@@ -251,7 +251,7 @@ pub fn appendRemaining(
251251 while (true) {
252252 try list.ensureUnusedCapacity(gpa, 1);
253253 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 &.{};
255255 const n = readVec(r, &.{ dest, additional_buffer }) catch |err| switch (err) {
256256 error.EndOfStream => break,
257257 error.ReadFailed => return error.ReadFailed,
......@@ -276,7 +276,7 @@ pub fn appendRemaining(
276276/// The reader's internal logical seek position moves forward in accordance
277277/// with the number of bytes returned from this function.
278278pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
279 return readVec(r, data, .unlimited);
279 return readVecLimit(r, data, .unlimited);
280280}
281281
282282/// 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 {
290290 comptime assert(@intFromEnum(Limit.unlimited) == std.math.maxInt(usize));
291291 var remaining = @intFromEnum(limit);
292292 for (data, 0..) |buf, i| {
293 const buffered = r.buffer[r.seek..r.end];
294 const copy_len = @min(buffered.len, buf.len, remaining);
295 @memcpy(buf[0..copy_len], buffered[0..copy_len]);
293 const buffer_contents = r.buffer[r.seek..r.end];
294 const copy_len = @min(buffer_contents.len, buf.len, remaining);
295 @memcpy(buf[0..copy_len], buffer_contents[0..copy_len]);
296296 r.seek += copy_len;
297297 remaining -= copy_len;
298298 if (remaining == 0) break;
......@@ -354,7 +354,7 @@ pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
354354 return @intFromEnum(limit) - remaining;
355355}
356356
357pub fn bufferContents(r: *Reader) []u8 {
357pub fn buffered(r: *Reader) []u8 {
358358 return r.buffer[r.seek..r.end];
359359}
360360
lib/std/io/Writer.zig+137-16
......@@ -57,8 +57,9 @@ pub const VTable = struct {
5757 /// Copies contents from an open file to the logical sink. `buffer[0..end]`
5858 /// is consumed first, followed by `limit` bytes from `file_reader`.
5959 ///
60 /// Number of bytes actually written is returned, excluding bytes from
61 /// `buffer`. Bytes from `buffer` are tracked by modifying `end`.
60 /// Number of bytes logically written is returned. This excludes bytes from
61 /// `buffer` because they have already been logically written. Number of
62 /// bytes consumed from `buffer` are tracked by modifying `end`.
6263 ///
6364 /// Number of bytes returned may be zero, which does not necessarily mean
6465 /// 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
162163 assert(data.len > 0);
163164 const buffer = w.buffer;
164165 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 }
166171 w.count += count;
167172 for (data) |bytes| {
168173 @memcpy(buffer[w.end..][0..bytes.len], bytes);
......@@ -213,6 +218,18 @@ pub fn flush(w: *Writer) Error!void {
213218 assert(0 == try w.vtable.drain(w, &.{}, 0));
214219}
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
216233pub fn unusedCapacitySlice(w: *const Writer) []u8 {
217234 return w.buffer[w.end..];
218235}
......@@ -246,13 +263,30 @@ pub fn writableSlice(w: *Writer, len: usize) Error![]u8 {
246263/// If `minimum_length` is zero, this is equivalent to `unusedCapacitySlice`.
247264pub fn writableSliceGreedy(w: *Writer, minimum_length: usize) Error![]u8 {
248265 assert(w.buffer.len >= minimum_length);
249 while (true) {
250 const cap_slice = w.buffer[w.end..];
251 if (cap_slice.len >= minimum_length) {
252 @branchHint(.likely);
253 return cap_slice;
254 }
266 while (w.buffer.len - w.end < minimum_length) {
255267 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..];
256290 }
257291}
258292
......@@ -376,20 +410,56 @@ pub fn write(w: *Writer, bytes: []const u8) Error!usize {
376410 w.count += bytes.len;
377411 return bytes.len;
378412 }
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;
380437}
381438
382/// Calls `write` as many times as necessary such that all of `bytes` are
439/// Calls `drain` as many times as necessary such that all of `bytes` are
383440/// transferred.
384441pub fn writeAll(w: *Writer, bytes: []const u8) Error!void {
385442 var index: usize = 0;
386443 while (index < bytes.len) index += try w.write(bytes[index..]);
387444}
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
389458pub fn print(w: *Writer, comptime format: []const u8, args: anytype) Error!void {
390459 try std.fmt.format(w, format, args);
391460}
392461
462/// Calls `drain` as many times as necessary such that `byte` is transferred.
393463pub fn writeByte(w: *Writer, byte: u8) Error!void {
394464 while (w.buffer.len - w.end == 0) {
395465 const n = try w.vtable.drain(w, &.{&.{byte}}, 1);
......@@ -405,6 +475,19 @@ pub fn writeByte(w: *Writer, byte: u8) Error!void {
405475 }
406476}
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
408491/// Writes the same byte many times, performing the underlying write call as
409492/// many times as necessary.
410493pub 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 {
496579/// See `sendFileReading` for an alternative that does not have
497580/// `error.Unimplemented` in the error set.
498581pub fn sendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) FileError!usize {
499 const end = w.end;
500 const n = try w.vtable.sendFile(w, file_reader, limit);
501 return n -| end;
582 return w.vtable.sendFile(w, file_reader, limit);
583}
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;
502619}
503620
504621/// Asserts nonzero buffer capacity.
......@@ -1696,8 +1813,12 @@ pub fn discardingSendFile(w: *Writer, file_reader: *File.Reader, limit: Limit) F
16961813 }
16971814}
16981815
1699/// This function is used by `VTable.drain` function implementations to
1700/// implement partial drains.
1816/// Removes the first `n` bytes from `buffer` by shifting buffer contents,
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.
17011822pub fn consume(w: *Writer, n: usize) usize {
17021823 if (n < w.end) {
17031824 const remaining = w.buffer[n..w.end];
lib/std/net.zig+1-1
......@@ -2093,7 +2093,7 @@ pub const Stream = struct {
20932093 },
20942094 .buffer = buffer,
20952095 },
2096 .file_writer = .initMode(stream.handle, &.{}, .streaming),
2096 .file_writer = .initMode(.{ .handle = stream.handle }, &.{}, .streaming),
20972097 };
20982098 }
20992099