authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-21 16:13:34-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:26-07:00
log57e1fc29d2728559928ae6f99ebe248c07f5ce18
tree6d7cae37db09693e5972b79f82ddf7fbec77acdf
parent5aa8573f2bc8fad297a63ce798ed5ede5b5833ac

reader interface vecs and bufferedwriter arraylist


14 files changed, 226 insertions(+), 196 deletions(-)

lib/std/compress/flate/bit_reader.zig+59-60
......@@ -2,17 +2,14 @@ const std = @import("std");
22const assert = std.debug.assert;
33const testing = std.testing;
44
5pub fn bitReader(comptime T: type, reader: anytype) BitReader(T, @TypeOf(reader)) {
6 return BitReader(T, @TypeOf(reader)).init(reader);
7}
8
9pub fn BitReader64(comptime ReaderType: type) type {
10 return BitReader(u64, ReaderType);
11}
12
13pub fn BitReader32(comptime ReaderType: type) type {
14 return BitReader(u32, ReaderType);
15}
5pub const Flags = packed struct(u3) {
6 /// dont advance internal buffer, just get bits, leave them in buffer
7 peek: bool = false,
8 /// assume that there is no need to fill, fill should be called before
9 buffered: bool = false,
10 /// bit reverse read bits
11 reverse: bool = false,
12};
1613
1714/// Bit reader used during inflate (decompression). Has internal buffer of 64
1815/// bits which shifts right after bits are consumed. Uses forward_reader to fill
......@@ -23,14 +20,14 @@ pub fn BitReader32(comptime ReaderType: type) type {
2320/// fill buffer from forward_reader by calling fill in advance and readF with
2421/// buffered flag set.
2522///
26pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
23pub fn BitReader(comptime T: type) type {
2724 assert(T == u32 or T == u64);
2825 const t_bytes: usize = @sizeOf(T);
2926 const Tshift = if (T == u64) u6 else u5;
3027
3128 return struct {
3229 // Underlying reader used for filling internal bits buffer
33 forward_reader: ReaderType = undefined,
30 forward_reader: *std.io.BufferedReader,
3431 // Internal buffer of 64 bits
3532 bits: T = 0,
3633 // Number of bits in the buffer
......@@ -38,10 +35,8 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
3835
3936 const Self = @This();
4037
41 pub const Error = ReaderType.Error || error{EndOfStream};
42
43 pub fn init(rdr: ReaderType) Self {
44 var self = Self{ .forward_reader = rdr };
38 pub fn init(forward_reader: *std.io.BufferedReader) Self {
39 var self = Self{ .forward_reader = forward_reader };
4540 self.fill(1) catch {};
4641 return self;
4742 }
......@@ -55,7 +50,7 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
5550 /// bits to decode. So `nice` is not hard limit, it will just try to have
5651 /// that number of bits available. If end of forward stream is reached
5752 /// it may be some extra zero bits in buffer.
58 pub inline fn fill(self: *Self, nice: u6) !void {
53 pub fn fill(self: *Self, nice: u6) !void {
5954 if (self.nbits >= nice and nice != 0) {
6055 return; // We have enough bits
6156 }
......@@ -86,31 +81,29 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
8681 // First read from internal bits buffer.
8782 var n: usize = 0;
8883 while (self.nbits > 0 and n < buf.len) {
89 buf[n] = try self.readF(u8, flag.buffered);
84 buf[n] = try self.readF(u8, .{ .buffered = true });
9085 n += 1;
9186 }
9287 // Then use forward reader for all other bytes.
9388 try self.forward_reader.readNoEof(buf[n..]);
9489 }
9590
96 pub const flag = struct {
97 pub const peek: u3 = 0b001; // dont advance internal buffer, just get bits, leave them in buffer
98 pub const buffered: u3 = 0b010; // assume that there is no need to fill, fill should be called before
99 pub const reverse: u3 = 0b100; // bit reverse read bits
100 };
101
10291 /// Alias for readF(U, 0).
10392 pub fn read(self: *Self, comptime U: type) !U {
10493 return self.readF(U, 0);
10594 }
10695
10796 /// Alias for readF with flag.peak set.
108 pub inline fn peekF(self: *Self, comptime U: type, comptime how: u3) !U {
109 return self.readF(U, how | flag.peek);
97 pub inline fn peekF(self: *Self, comptime U: type, comptime how: Flags) !U {
98 return self.readF(U, .{
99 .peek = true,
100 .buffered = how.buffered,
101 .reverse = how.reverse,
102 });
110103 }
111104
112105 /// Read with flags provided.
113 pub fn readF(self: *Self, comptime U: type, comptime how: u3) !U {
106 pub fn readF(self: *Self, comptime U: type, comptime how: Flags) !U {
114107 if (U == T) {
115108 assert(how == 0);
116109 assert(self.alignBits() == 0);
......@@ -129,34 +122,35 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
129122 try self.shift(n); // advance buffer for n
130123 return u;
131124 },
132 (flag.peek) => { // no shift, leave bits in the buffer
125 .{ .peek = true } => { // no shift, leave bits in the buffer
133126 try self.fill(n);
134127 return @truncate(self.bits);
135128 },
136 flag.buffered => { // no fill, assume that buffer has enough bits
129 .{ .buffered = true } => { // no fill, assume that buffer has enough bits
137130 const u: U = @truncate(self.bits);
138131 try self.shift(n);
139132 return u;
140133 },
141 (flag.reverse) => { // same as 0 with bit reverse
134 .{ .reverse = true } => { // same as 0 with bit reverse
142135 try self.fill(n);
143136 const u: U = @truncate(self.bits);
144137 try self.shift(n);
145138 return @bitReverse(u);
146139 },
147 (flag.peek | flag.reverse) => {
140 .{ .peek = true, .reverse = true } => {
148141 try self.fill(n);
149142 return @bitReverse(@as(U, @truncate(self.bits)));
150143 },
151 (flag.buffered | flag.reverse) => {
144 .{ .buffered = true, .reverse = true } => {
152145 const u: U = @truncate(self.bits);
153146 try self.shift(n);
154147 return @bitReverse(u);
155148 },
156 (flag.peek | flag.buffered) => {
149 .{ .peek = true, .buffered = true },
150 => {
157151 return @truncate(self.bits);
158152 },
159 (flag.peek | flag.buffered | flag.reverse) => {
153 .{ .peek = true, .buffered = true, .reverse = true } => {
160154 return @bitReverse(@as(U, @truncate(self.bits)));
161155 },
162156 }
......@@ -169,7 +163,7 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
169163 0 => {
170164 try self.fill(n);
171165 },
172 flag.buffered => {},
166 .{ .buffered = true } => {},
173167 else => unreachable,
174168 }
175169 const mask: u16 = (@as(u16, 1) << n) - 1;
......@@ -226,24 +220,24 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
226220 /// 11000111
227221 pub fn readFixedCode(self: *Self) !u16 {
228222 try self.fill(7 + 2);
229 const code7 = try self.readF(u7, flag.buffered | flag.reverse);
223 const code7 = try self.readF(u7, .{ .buffered = true, .reverse = true });
230224 if (code7 <= 0b0010_111) { // 7 bits, 256-279, codes 0000_000 - 0010_111
231225 return @as(u16, code7) + 256;
232226 } else if (code7 <= 0b1011_111) { // 8 bits, 0-143, codes 0011_0000 through 1011_1111
233 return (@as(u16, code7) << 1) + @as(u16, try self.readF(u1, flag.buffered)) - 0b0011_0000;
227 return (@as(u16, code7) << 1) + @as(u16, try self.readF(u1, .{ .buffered = true })) - 0b0011_0000;
234228 } else if (code7 <= 0b1100_011) { // 8 bit, 280-287, codes 1100_0000 - 1100_0111
235 return (@as(u16, code7 - 0b1100000) << 1) + try self.readF(u1, flag.buffered) + 280;
229 return (@as(u16, code7 - 0b1100000) << 1) + try self.readF(u1, .{ .buffered = true }) + 280;
236230 } else { // 9 bit, 144-255, codes 1_1001_0000 - 1_1111_1111
237 return (@as(u16, code7 - 0b1100_100) << 2) + @as(u16, try self.readF(u2, flag.buffered | flag.reverse)) + 144;
231 return (@as(u16, code7 - 0b1100_100) << 2) + @as(u16, try self.readF(u2, .{ .buffered = true, .reverse = true })) + 144;
238232 }
239233 }
240234 };
241235}
242236
243237test "readF" {
244 var fbs = std.io.fixedBufferStream(&[_]u8{ 0xf3, 0x48, 0xcd, 0xc9, 0x00, 0x00 });
245 var br = bitReader(u64, fbs.reader());
246 const F = BitReader64(@TypeOf(fbs.reader())).flag;
238 var input: std.io.BufferedReader = undefined;
239 input.initFixed(&[_]u8{ 0xf3, 0x48, 0xcd, 0xc9, 0x00, 0x00 });
240 var br: BitReader(u64) = .init(&input);
247241
248242 try testing.expectEqual(@as(u8, 48), br.nbits);
249243 try testing.expectEqual(@as(u64, 0xc9cd48f3), br.bits);
......@@ -253,8 +247,8 @@ test "readF" {
253247 try testing.expectEqual(@as(u8, 48 - 3), br.nbits);
254248 try testing.expectEqual(@as(u3, 5), br.alignBits());
255249
256 try testing.expect(try br.readF(u8, F.peek) == 0b0001_1110);
257 try testing.expect(try br.readF(u9, F.peek) == 0b1_0001_1110);
250 try testing.expect(try br.readF(u8, .{ .peek = true }) == 0b0001_1110);
251 try testing.expect(try br.readF(u9, .{ .peek = true }) == 0b1_0001_1110);
258252 try br.shift(9);
259253 try testing.expectEqual(@as(u8, 36), br.nbits);
260254 try testing.expectEqual(@as(u3, 4), br.alignBits());
......@@ -283,15 +277,15 @@ test "read block type 1 data" {
283277 0x0c, 0x01, 0x02, 0x03, //
284278 0xaa, 0xbb, 0xcc, 0xdd,
285279 };
286 var fbs = std.io.fixedBufferStream(&data);
287 var br = bitReader(T, fbs.reader());
288 const F = BitReader(T, @TypeOf(fbs.reader())).flag;
280 var fbs: std.io.BufferedReader = undefined;
281 fbs.initFixed(&data);
282 var br: BitReader(T) = .init(&fbs);
289283
290284 try testing.expectEqual(@as(u1, 1), try br.readF(u1, 0)); // bfinal
291285 try testing.expectEqual(@as(u2, 1), try br.readF(u2, 0)); // block_type
292286
293287 for ("Hello world\n") |c| {
294 try testing.expectEqual(@as(u8, c), try br.readF(u8, F.reverse) - 0x30);
288 try testing.expectEqual(@as(u8, c), try br.readF(u8, .{ .reverse = true }) - 0x30);
295289 }
296290 try testing.expectEqual(@as(u7, 0), try br.readF(u7, 0)); // end of block
297291 br.alignToByte();
......@@ -306,8 +300,9 @@ test "shift/fill" {
306300 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
307301 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
308302 };
309 var fbs = std.io.fixedBufferStream(&data);
310 var br = bitReader(u64, fbs.reader());
303 var fbs: std.io.BufferedReader = undefined;
304 fbs.initFixed(&data);
305 var br: BitReader(u64) = .init(&fbs);
311306
312307 try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits);
313308 try br.shift(8);
......@@ -332,8 +327,9 @@ test "readAll" {
332327 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
333328 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
334329 };
335 var fbs = std.io.fixedBufferStream(&data);
336 var br = bitReader(T, fbs.reader());
330 var fbs: std.io.BufferedReader = undefined;
331 fbs.initFixed(&data);
332 var br: BitReader(T) = .init(&fbs);
337333
338334 switch (T) {
339335 u64 => try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits),
......@@ -354,8 +350,9 @@ test "readFixedCode" {
354350 inline for ([_]type{ u64, u32 }) |T| {
355351 const fixed_codes = @import("huffman_encoder.zig").fixed_codes;
356352
357 var fbs = std.io.fixedBufferStream(&fixed_codes);
358 var rdr = bitReader(T, fbs.reader());
353 var fbs: std.io.BufferedReader = undefined;
354 fbs.initFixed(&fixed_codes);
355 var rdr: BitReader(T) = .init(&fbs);
359356
360357 for (0..286) |c| {
361358 try testing.expectEqual(c, try rdr.readFixedCode());
......@@ -369,8 +366,9 @@ test "u32 leaves no bits on u32 reads" {
369366 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
370367 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
371368 };
372 var fbs = std.io.fixedBufferStream(&data);
373 var br = bitReader(u32, fbs.reader());
369 var fbs: std.io.BufferedReader = undefined;
370 fbs.initFixed(&data);
371 var br: BitReader(u32) = .init(&fbs);
374372
375373 _ = try br.read(u3);
376374 try testing.expectEqual(29, br.nbits);
......@@ -396,8 +394,9 @@ test "u64 need fill after alignToByte" {
396394 };
397395
398396 // without fill
399 var fbs = std.io.fixedBufferStream(&data);
400 var br = bitReader(u64, fbs.reader());
397 var fbs: std.io.BufferedReader = undefined;
398 fbs.initFixed(&data);
399 var br: BitReader(u64) = .init(&fbs);
401400 _ = try br.read(u23);
402401 try testing.expectEqual(41, br.nbits);
403402 br.alignToByte();
......@@ -409,7 +408,7 @@ test "u64 need fill after alignToByte" {
409408
410409 // fill after align ensures all bits filled
411410 fbs.reset();
412 br = bitReader(u64, fbs.reader());
411 br = .init(&fbs);
413412 _ = try br.read(u23);
414413 try testing.expectEqual(41, br.nbits);
415414 br.alignToByte();
lib/std/compress/lzma2.zig+4-8
......@@ -3,11 +3,7 @@ const Allocator = std.mem.Allocator;
33
44pub const decode = @import("lzma2/decode.zig");
55
6pub fn decompress(
7 allocator: Allocator,
8 reader: anytype,
9 writer: anytype,
10) !void {
6pub fn decompress(allocator: Allocator, reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) !void {
117 var decoder = try decode.Decoder.init(allocator);
128 defer decoder.deinit(allocator);
139 return decoder.decompress(allocator, reader, writer);
......@@ -19,11 +15,11 @@ test {
1915 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02,
2016 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00,
2117 };
22 var stream: std.io.FixedBufferStream = .{ .buffer = compressed };
23
18 var stream: std.io.BufferedReader = undefined;
19 stream.initFixed(&compressed);
2420 var decomp: std.io.AllocatingWriter = undefined;
2521 const decomp_bw = decomp.init(std.testing.allocator);
2622 defer decomp.deinit();
27 try decompress(std.testing.allocator, stream.reader(), decomp_bw);
23 try decompress(std.testing.allocator, &stream, decomp_bw);
2824 try std.testing.expectEqualSlices(u8, expected, decomp.getWritten());
2925}
lib/std/debug/Dwarf.zig+6-5
......@@ -2235,14 +2235,15 @@ pub const ElfModule = struct {
22352235
22362236 const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
22372237 sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {
2238 var section_stream: std.io.FixedBufferStream = .{ .buffer = section_bytes };
2239 const section_reader = section_stream.reader();
2240 const chdr = section_reader.readStruct(elf.Chdr) catch continue;
2238 var section_reader: std.io.BufferedReader = undefined;
2239 section_reader.initFixed(section_bytes);
2240 const chdr = section_reader.takeStruct(elf.Chdr) catch continue;
22412241 if (chdr.ch_type != .ZLIB) continue;
2242 const ch_size = chdr.ch_size;
22422243
2243 var zlib_stream = std.compress.zlib.decompressor(section_reader);
2244 var zlib_stream = std.compress.zlib.decompressor(&section_reader);
22442245
2245 const decompressed_section = try gpa.alloc(u8, chdr.ch_size);
2246 const decompressed_section = try gpa.alloc(u8, ch_size);
22462247 errdefer gpa.free(decompressed_section);
22472248
22482249 const read = zlib_stream.reader().readAll(decompressed_section) catch continue;
lib/std/fs/File.zig+34-6
......@@ -1590,8 +1590,10 @@ pub fn reader(file: File) std.io.Reader {
15901590 return .{
15911591 .context = handleToOpaque(file.handle),
15921592 .vtable = .{
1593 .seekRead = reader_seekRead,
1593 .posRead = reader_posRead,
1594 .posReadVec = reader_posReadVec,
15941595 .streamRead = reader_streamRead,
1596 .streamReadVec = reader_streamReadVec,
15951597 },
15961598 };
15971599}
......@@ -1610,7 +1612,7 @@ pub fn writer(file: File) std.io.Writer {
16101612/// vectors through the underlying write calls as possible.
16111613const max_buffers_len = 16;
16121614
1613pub fn reader_seekRead(
1615pub fn reader_posRead(
16141616 context: *anyopaque,
16151617 bw: *std.io.BufferedWriter,
16161618 limit: std.io.Reader.Limit,
......@@ -1621,10 +1623,36 @@ pub fn reader_seekRead(
16211623 return writer.writeFile(bw, file, .init(offset), len, &.{}, 0);
16221624}
16231625
1624pub fn reader_streamRead(context: *anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Reader.Limit) anyerror!usize {
1626pub fn reader_posReadVec(context: *anyopaque, data: []const []u8, offset: u64) anyerror!std.io.Reader.Status {
1627 const file = opaqueToHandle(context);
1628 const n = try file.preadv(data, offset);
1629 return .{
1630 .len = n,
1631 .end = n == 0,
1632 };
1633}
1634
1635pub fn reader_streamRead(
1636 context: *anyopaque,
1637 bw: *std.io.BufferedWriter,
1638 limit: std.io.Reader.Limit,
1639) anyerror!std.io.Reader.Status {
16251640 const file = opaqueToHandle(context);
16261641 const len: std.io.Writer.Len = if (limit.unwrap()) |l| .init(l) else .entire_file;
1627 return writer.writeFile(bw, file, .none, len, &.{}, 0);
1642 const n = try writer.writeFile(bw, file, .none, len, &.{}, 0);
1643 return .{
1644 .len = n,
1645 .end = n == 0,
1646 };
1647}
1648
1649pub fn reader_streamReadVec(context: *anyopaque, data: []const []u8) anyerror!std.io.Reader.Status {
1650 const file = opaqueToHandle(context);
1651 const n = try file.readv(data);
1652 return .{
1653 .len = n,
1654 .end = n == 0,
1655 };
16281656}
16291657
16301658pub fn writer_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
......@@ -1671,7 +1699,7 @@ pub fn writer_writeFile(
16711699 context: *anyopaque,
16721700 in_file: std.fs.File,
16731701 in_offset: u64,
1674 in_len: std.io.Writer.VTable.FileLen,
1702 in_len: std.io.Writer.FileLen,
16751703 headers_and_trailers: []const []const u8,
16761704 headers_len: usize,
16771705) anyerror!usize {
......@@ -1705,7 +1733,7 @@ fn writeFileUnseekable(
17051733 out_fd: Handle,
17061734 in_fd: Handle,
17071735 in_offset: u64,
1708 in_len: std.io.Writer.VTable.FileLen,
1736 in_len: std.io.Writer.FileLen,
17091737 headers_and_trailers: []const []const u8,
17101738 headers_len: usize,
17111739) anyerror!usize {
lib/std/http/Client.zig+2-2
......@@ -1119,7 +1119,7 @@ pub const Request = struct {
11191119 context: *anyopaque,
11201120 file: std.fs.File,
11211121 offset: u64,
1122 len: std.io.Writer.VTable.FileLen,
1122 len: std.io.Writer.FileLen,
11231123 headers_and_trailers: []const []const u8,
11241124 headers_len: usize,
11251125 ) anyerror!usize {
......@@ -1159,7 +1159,7 @@ pub const Request = struct {
11591159 context: *anyopaque,
11601160 file: std.fs.File,
11611161 offset: u64,
1162 len: std.io.Writer.VTable.FileLen,
1162 len: std.io.Writer.FileLen,
11631163 headers_and_trailers: []const []const u8,
11641164 headers_len: usize,
11651165 ) anyerror!usize {
lib/std/http/Server.zig+2-2
......@@ -881,7 +881,7 @@ pub const Response = struct {
881881 context: *anyopaque,
882882 file: std.fs.File,
883883 offset: u64,
884 len: std.io.Writer.VTable.FileLen,
884 len: std.io.Writer.FileLen,
885885 headers_and_trailers: []const []const u8,
886886 headers_len: usize,
887887 ) anyerror!usize {
......@@ -948,7 +948,7 @@ pub const Response = struct {
948948 context: *anyopaque,
949949 file: std.fs.File,
950950 offset: u64,
951 len: std.io.Writer.VTable.FileLen,
951 len: std.io.Writer.FileLen,
952952 headers_and_trailers: []const []const u8,
953953 headers_len: usize,
954954 ) anyerror!usize {
lib/std/io.zig+1-1
......@@ -118,7 +118,7 @@ fn null_writeFile(
118118 context: *anyopaque,
119119 file: std.fs.File,
120120 offset: u64,
121 len: Writer.VTable.FileLen,
121 len: Writer.FileLen,
122122 headers_and_trailers: []const []const u8,
123123 headers_len: usize,
124124) anyerror!usize {
lib/std/io/AllocatingWriter.zig+1-1
......@@ -153,7 +153,7 @@ fn writeFile(
153153 context: *anyopaque,
154154 file: std.fs.File,
155155 offset: u64,
156 len: std.io.Writer.VTable.FileLen,
156 len: std.io.Writer.FileLen,
157157 headers_and_trailers_full: []const []const u8,
158158 headers_len_full: usize,
159159) anyerror!usize {
lib/std/io/BufferedReader.zig+21-4
......@@ -25,7 +25,7 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
2525 .context = br,
2626 .vtable = &.{
2727 .streamRead = null,
28 .seekRead = null,
28 .posRead = null,
2929 },
3030 },
3131 };
......@@ -44,7 +44,9 @@ pub fn reader(br: *BufferedReader) Reader {
4444 .context = br,
4545 .vtable = &.{
4646 .streamRead = passthru_streamRead,
47 .seekRead = passthru_seekRead,
47 .streamReadVec = passthru_streamReadVec,
48 .posRead = passthru_posRead,
49 .posReadVec = passthru_posReadVec,
4850 },
4951 };
5052}
......@@ -65,14 +67,29 @@ fn passthru_streamRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit
6567 return br.unbuffered_reader.streamRead(bw, limit);
6668}
6769
68fn passthru_seekRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit, off: u64) anyerror!Reader.Status {
70fn passthru_streamReadVec(ctx: *anyopaque, data: []const []u8) anyerror!Reader.Status {
71 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
72 _ = br;
73 _ = data;
74 @panic("TODO");
75}
76
77fn passthru_posRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit, off: u64) anyerror!Reader.Status {
6978 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
7079 const buffer = br.storage.buffer.items;
7180 if (off < buffer.len) {
7281 const send = buffer[off..limit.min(buffer.len)];
7382 return bw.writeSplat(send, 1);
7483 }
75 return br.unbuffered_reader.seekRead(bw, limit, off - buffer.len);
84 return br.unbuffered_reader.posRead(bw, limit, off - buffer.len);
85}
86
87fn passthru_posReadVec(ctx: *anyopaque, data: []const []u8, off: u64) anyerror!Reader.Status {
88 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
89 _ = br;
90 _ = data;
91 _ = off;
92 @panic("TODO");
7693}
7794
7895/// Returns the next `n` bytes from `unbuffered_reader`, filling the buffer as
lib/std/io/BufferedWriter.zig+78-91
......@@ -10,30 +10,15 @@ const testing = std.testing;
1010///
1111/// If this has capacity zero, the writer is unbuffered, and `flush` is a no-op.
1212buffer: std.ArrayListUnmanaged(u8),
13mode: union(enum) {
14 /// Return `error.NoSpaceLeft` if a write could not fit into the buffer.
15 fixed,
16 /// Underlying stream to send bytes to.
17 ///
18 /// A write will only be sent here if it could not fit into `buffer`, or if
19 /// it is a `writeFile`.
20 ///
21 /// `unbuffered_writer` may modify `buffer` if the number of bytes returned
22 /// equals number of bytes provided. This property is exploited by
23 /// `std.io.AllocatingWriter` for example.
24 writer: Writer,
25 /// If this is provided, `buffer` will grow superlinearly rather than
26 /// become full.
27 allocator: Allocator,
28},
29
30pub fn deinit(bw: *BufferedWriter) void {
31 switch (bw.mode) {
32 .allocator => |gpa| bw.buffer.deinit(gpa),
33 .fixed, .writer => {},
34 }
35 bw.* = undefined;
36}
13/// Underlying stream to send bytes to.
14///
15/// A write will only be sent here if it could not fit into `buffer`, or if it
16/// is a `writeFile`.
17///
18/// `unbuffered_writer` may modify `buffer` if the number of bytes returned
19/// equals number of bytes provided. This property is exploited by
20/// `std.io.AllocatingWriter` for example.
21unbuffered_writer: Writer,
3722
3823/// Number of slices to store on the stack, when trying to send as many byte
3924/// vectors through the underlying write calls as possible.
......@@ -72,23 +57,24 @@ pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {
7257/// This function is available when using `initFixed`.
7358pub fn getWritten(bw: *const BufferedWriter) []u8 {
7459 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
75 return bw.buffer[0..bw.end];
60 return bw.buffer.items;
7661}
7762
7863/// This function is available when using `initFixed`.
7964pub fn reset(bw: *BufferedWriter) void {
8065 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
81 bw.end = 0;
66 bw.buffer.items.len = 0;
8267}
8368
8469pub fn flush(bw: *BufferedWriter) anyerror!void {
85 const send_buffer = bw.buffer[0..bw.end];
70 const list = &bw.buffer;
71 const send_buffer = list.items;
8672 try bw.unbuffered_writer.writeAll(send_buffer);
87 bw.end = 0;
73 list.items.len = 0;
8874}
8975
9076pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
91 return bw.buffer[bw.end..];
77 return bw.buffer.unusedCapacitySlice();
9278}
9379
9480/// The `data` parameter is mutable because this function needs to mutate the
......@@ -116,11 +102,12 @@ pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {
116102
117103fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
118104 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
119 const buffer = bw.buffer;
120 const start_end = bw.end;
105 const list = &bw.buffer;
106 const buffer = list.allocatedSlice();
107 const start_end = list.items.len;
121108
122109 var buffers: [max_buffers_len][]const u8 = undefined;
123 var end = bw.end;
110 var end = start_end;
124111 for (data, 0..) |bytes, i| {
125112 const new_end = end + bytes.len;
126113 if (new_end <= buffer.len) {
......@@ -144,10 +131,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
144131 @branchHint(.unlikely);
145132 const remainder = buffer[n..end];
146133 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
147 bw.end = remainder.len;
134 list.items.len = remainder.len;
148135 return end - start_end;
149136 }
150 bw.end = 0;
137 list.items.len = 0;
151138 return n - start_end;
152139 }
153140 const n = try bw.unbuffered_writer.writeSplat(send_buffers, 1);
......@@ -155,10 +142,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
155142 @branchHint(.unlikely);
156143 const remainder = buffer[n..end];
157144 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
158 bw.end = remainder.len;
145 list.items.len = remainder.len;
159146 return end - start_end;
160147 }
161 bw.end = 0;
148 list.items.len = 0;
162149 return n - start_end;
163150 }
164151
......@@ -168,7 +155,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
168155 @branchHint(.unlikely);
169156 // It was added in the loop above; undo it here.
170157 end -= pattern.len;
171 bw.end = end;
158 list.items.len = end;
172159 return end - start_end;
173160 }
174161
......@@ -176,7 +163,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
176163
177164 switch (pattern.len) {
178165 0 => {
179 bw.end = end;
166 list.items.len = end;
180167 return end - start_end;
181168 },
182169 1 => {
......@@ -184,7 +171,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
184171 if (new_end <= buffer.len) {
185172 @branchHint(.likely);
186173 @memset(buffer[end..new_end], pattern[0]);
187 bw.end = new_end;
174 list.items.len = new_end;
188175 return new_end - start_end;
189176 }
190177 buffers[0] = buffer[0..end];
......@@ -194,10 +181,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
194181 @branchHint(.unlikely);
195182 const remainder = buffer[n..end];
196183 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
197 bw.end = remainder.len;
184 list.items.len = remainder.len;
198185 return end - start_end;
199186 }
200 bw.end = 0;
187 list.items.len = 0;
201188 return n - start_end;
202189 },
203190 else => {
......@@ -207,7 +194,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
207194 while (end < new_end) : (end += pattern.len) {
208195 @memcpy(buffer[end..][0..pattern.len], pattern);
209196 }
210 bw.end = new_end;
197 list.items.len = new_end;
211198 return new_end - start_end;
212199 }
213200 buffers[0] = buffer[0..end];
......@@ -217,10 +204,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
217204 @branchHint(.unlikely);
218205 const remainder = buffer[n..end];
219206 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
220 bw.end = remainder.len;
207 list.items.len = remainder.len;
221208 return end - start_end;
222209 }
223 bw.end = 0;
210 list.items.len = 0;
224211 return n - start_end;
225212 },
226213 }
......@@ -228,12 +215,14 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
228215
229216fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize {
230217 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
218 const list = &bw.buffer;
231219 // When this function is called it means the buffer got full, so it's time
232220 // to return an error. However, we still need to make sure all of the
233221 // available buffer has been used.
234222 const first = data[0];
235 const dest = bw.buffer[bw.end..];
223 const dest = list.unusedCapacitySlice();
236224 @memcpy(dest, first[0..dest.len]);
225 list.items.len = list.capacity;
237226 return error.NoSpaceLeft;
238227}
239228
......@@ -242,26 +231,29 @@ fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize {
242231/// available buffer has been filled.
243232fn fixed_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
244233 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
234 const list = &bw.buffer;
245235 for (data) |bytes| {
246 const dest = bw.buffer[bw.end..];
236 const dest = list.unusedCapacitySlice();
247237 if (dest.len == 0) return error.NoSpaceLeft;
248238 const len = @min(bytes.len, dest.len);
249239 @memcpy(dest[0..len], bytes[0..len]);
250 bw.end += len;
240 list.items.len += len;
251241 }
252242 const pattern = data[data.len - 1];
253 const dest = bw.buffer[bw.end..];
243 const dest = list.unusedCapacitySlice();
254244 switch (pattern.len) {
255245 0 => unreachable,
256246 1 => @memset(dest, pattern[0]),
257247 else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern),
258248 }
249 list.items.len = list.capacity;
259250 return error.NoSpaceLeft;
260251}
261252
262253pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
263 const buffer = bw.buffer;
264 const end = bw.end;
254 const list = &bw.buffer;
255 const buffer = list.allocatedSlice();
256 const end = list.items.len;
265257 const new_end = end + bytes.len;
266258 if (new_end > buffer.len) {
267259 var data: [2][]const u8 = .{ buffer[0..end], bytes };
......@@ -270,14 +262,14 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
270262 @branchHint(.unlikely);
271263 const remainder = buffer[n..end];
272264 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
273 bw.end = remainder.len;
265 list.items.len = remainder.len;
274266 return 0;
275267 }
276 bw.end = 0;
268 list.items.len = 0;
277269 return n - end;
278270 }
279271 @memcpy(buffer[end..new_end], bytes);
280 bw.end = new_end;
272 list.items.len = new_end;
281273 return bytes.len;
282274}
283275
......@@ -302,35 +294,29 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
302294 list.items.len = buffer.len + 1;
303295 return;
304296 }
305 switch (bw.mode) {
306 .fixed => return error.NoSpaceLeft,
307 .writer => |w| {
308 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
309 while (true) {
310 const n = try w.writev(&buffers);
311 if (n == 0) {
312 @branchHint(.unlikely);
313 continue;
314 } else if (n >= buffer.len) {
315 @branchHint(.likely);
316 if (n > buffer.len) {
317 @branchHint(.likely);
318 list.items.len = 0;
319 return;
320 } else {
321 buffer[0] = byte;
322 list.items.len = 1;
323 return;
324 }
325 }
326 const remainder = buffer[n..];
327 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
328 buffer[remainder.len] = byte;
329 list.items.len = remainder.len + 1;
297 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
298 while (true) {
299 const n = try bw.unbuffered_writer.writev(&buffers);
300 if (n == 0) {
301 @branchHint(.unlikely);
302 continue;
303 } else if (n >= buffer.len) {
304 @branchHint(.likely);
305 if (n > buffer.len) {
306 @branchHint(.likely);
307 list.items.len = 0;
308 return;
309 } else {
310 buffer[0] = byte;
311 list.items.len = 1;
330312 return;
331313 }
332 },
333 .allocator => |gpa| try list.append(gpa, byte),
314 }
315 const remainder = buffer[n..];
316 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
317 buffer[remainder.len] = byte;
318 list.items.len = remainder.len + 1;
319 return;
334320 }
335321}
336322
......@@ -395,7 +381,7 @@ pub fn writeFile(
395381 bw: *BufferedWriter,
396382 file: std.fs.File,
397383 offset: u64,
398 len: Writer.VTable.FileLen,
384 len: Writer.FileLen,
399385 headers_and_trailers: []const []const u8,
400386 headers_len: usize,
401387) anyerror!usize {
......@@ -406,14 +392,15 @@ fn passthru_writeFile(
406392 context: *anyopaque,
407393 file: std.fs.File,
408394 offset: u64,
409 len: Writer.VTable.FileLen,
395 len: Writer.FileLen,
410396 headers_and_trailers: []const []const u8,
411397 headers_len: usize,
412398) anyerror!usize {
413399 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
414 const buffer = bw.buffer;
400 const list = &bw.buffer;
401 const buffer = list.allocatedSlice();
415402 if (buffer.len == 0) return bw.unbuffered_writer.writeFile(file, offset, len, headers_and_trailers, headers_len);
416 const start_end = bw.end;
403 const start_end = list.items.len;
417404 const headers = headers_and_trailers[0..headers_len];
418405 const trailers = headers_and_trailers[headers_len..];
419406 var buffers: [max_buffers_len][]const u8 = undefined;
......@@ -443,10 +430,10 @@ fn passthru_writeFile(
443430 @branchHint(.unlikely);
444431 const remainder = buffer[n..end];
445432 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
446 bw.end = remainder.len;
433 list.items.len = remainder.len;
447434 return end - start_end;
448435 }
449 bw.end = 0;
436 list.items.len = 0;
450437 return n - start_end;
451438 }
452439 // Have not made it past the headers yet; must call `writev`.
......@@ -455,10 +442,10 @@ fn passthru_writeFile(
455442 @branchHint(.unlikely);
456443 const remainder = buffer[n..end];
457444 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
458 bw.end = remainder.len;
445 list.items.len = remainder.len;
459446 return end - start_end;
460447 }
461 bw.end = 0;
448 list.items.len = 0;
462449 return n - start_end;
463450 }
464451 // All headers written to buffer.
......@@ -473,10 +460,10 @@ fn passthru_writeFile(
473460 @branchHint(.unlikely);
474461 const remainder = buffer[n..end];
475462 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
476 bw.end = remainder.len;
463 list.items.len = remainder.len;
477464 return end - start_end;
478465 }
479 bw.end = 0;
466 list.items.len = 0;
480467 return n - start_end;
481468}
482469
......@@ -484,7 +471,7 @@ pub const WriteFileOptions = struct {
484471 offset: u64 = 0,
485472 /// If the size of the source file is known, it is likely that passing the
486473 /// size here will save one syscall.
487 len: Writer.VTable.FileLen = .entire_file,
474 len: Writer.FileLen = .entire_file,
488475 /// Headers and trailers must be passed together so that in case `len` is
489476 /// zero, they can be forwarded directly to `Writer.VTable.writev`.
490477 ///
lib/std/io/CountingWriter.zig+1-1
......@@ -33,7 +33,7 @@ fn passthru_writeFile(
3333 context: *anyopaque,
3434 file: std.fs.File,
3535 offset: u64,
36 len: Writer.VTable.FileLen,
36 len: Writer.FileLen,
3737 headers_and_trailers: []const []const u8,
3838 headers_len: usize,
3939) anyerror!usize {
lib/std/io/Reader.zig+13-11
......@@ -19,10 +19,11 @@ pub const VTable = struct {
1919 ///
2020 /// If this is `null` it is equivalent to always returning
2121 /// `error.Unseekable`.
22 seekRead: ?*const fn (ctx: *anyopaque, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) anyerror!Status,
22 posRead: ?*const fn (ctx: *anyopaque, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) anyerror!Status,
23 posReadVec: ?*const fn (ctx: *anyopaque, data: []const []u8, offset: u64) anyerror!Status,
2324
2425 /// Writes bytes from the internally tracked stream position to `bw`, or
25 /// returns `error.Unstreamable`, indicating `seekRead` should be used
26 /// returns `error.Unstreamable`, indicating `posRead` should be used
2627 /// instead.
2728 ///
2829 /// Returns the number of bytes written, which will be at minimum `0` and at
......@@ -37,9 +38,10 @@ pub const VTable = struct {
3738 /// If this is `null` it is equivalent to always returning
3839 /// `error.Unstreamable`.
3940 streamRead: ?*const fn (ctx: *anyopaque, bw: *std.io.BufferedWriter, limit: Limit) anyerror!Status,
41 streamReadVec: ?*const fn (ctx: *anyopaque, data: []const []u8) anyerror!Status,
4042};
4143
42pub const Len = @Type(.{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 });
44pub const Len = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } });
4345
4446pub const Status = packed struct(usize) {
4547 /// Number of bytes that were written to `writer`.
......@@ -56,7 +58,7 @@ pub const Limit = enum(usize) {
5658/// Returns total number of bytes written to `w`.
5759pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {
5860 if (r.vtable.pread != null) {
59 return seekReadAll(r, w) catch |err| switch (err) {
61 return posReadAll(r, w) catch |err| switch (err) {
6062 error.Unseekable => {},
6163 else => return err,
6264 };
......@@ -68,11 +70,11 @@ pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {
6870///
6971/// May return `error.Unseekable`, indicating this function cannot be used to
7072/// read from the reader.
71pub fn seekReadAll(r: Reader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize {
72 const vtable_seekRead = r.vtable.seekRead.?;
73pub fn posReadAll(r: Reader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize {
74 const vtable_posRead = r.vtable.posRead.?;
7375 var offset: u64 = start_offset;
7476 while (true) {
75 const status = try vtable_seekRead(r.context, w, .none, offset);
77 const status = try vtable_posRead(r.context, w, .none, offset);
7678 offset += status.len;
7779 if (status.end) return @intCast(offset - start_offset);
7880 }
......@@ -130,6 +132,10 @@ pub fn buffered(r: Reader, buffer: []u8) std.io.BufferedReader {
130132 };
131133}
132134
135pub fn unbuffered(r: Reader) std.io.BufferedReader {
136 return buffered(r, &.{});
137}
138
133139pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader {
134140 return .{
135141 .reader = r,
......@@ -140,10 +146,6 @@ pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader {
140146 };
141147}
142148
143pub fn unbuffered(r: Reader) std.io.BufferedReader {
144 return buffered(r, &.{});
145}
146
147149test "when the backing reader provides one byte at a time" {
148150 const OneByteReader = struct {
149151 str: []const u8,
lib/std/io/Writer.zig+3-3
......@@ -80,7 +80,7 @@ pub fn writeFile(
8080 w: Writer,
8181 file: std.fs.File,
8282 offset: u64,
83 len: VTable.FileLen,
83 len: FileLen,
8484 headers_and_trailers: []const []const u8,
8585 headers_len: usize,
8686) anyerror!usize {
......@@ -91,7 +91,7 @@ pub fn unimplemented_writeFile(
9191 context: *anyopaque,
9292 file: std.fs.File,
9393 offset: u64,
94 len: VTable.FileLen,
94 len: FileLen,
9595 headers_and_trailers: []const []const u8,
9696 headers_len: usize,
9797) anyerror!usize {
......@@ -107,7 +107,7 @@ pub fn unimplemented_writeFile(
107107pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
108108 return .{
109109 .buffer = .initBuffer(buffer),
110 .mode = .{ .writer = w },
110 .unbuffered_writer = w,
111111 };
112112}
113113
lib/std/net.zig+1-1
......@@ -2011,7 +2011,7 @@ pub const Stream = struct {
20112011 context: *anyopaque,
20122012 in_file: std.fs.File,
20132013 in_offset: u64,
2014 in_len: std.io.Writer.VTable.FileLen,
2014 in_len: std.io.Writer.FileLen,
20152015 headers_and_trailers: []const []const u8,
20162016 headers_len: usize,
20172017 ) anyerror!usize {