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");...@@ -2,17 +2,14 @@ const std = @import("std");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const testing = std.testing;3const testing = std.testing;
44
5pub fn bitReader(comptime T: type, reader: anytype) BitReader(T, @TypeOf(reader)) {5pub const Flags = packed struct(u3) {
6 return BitReader(T, @TypeOf(reader)).init(reader);6 /// dont advance internal buffer, just get bits, leave them in buffer
7}7 peek: bool = false,
88 /// assume that there is no need to fill, fill should be called before
9pub fn BitReader64(comptime ReaderType: type) type {9 buffered: bool = false,
10 return BitReader(u64, ReaderType);10 /// bit reverse read bits
11}11 reverse: bool = false,
1212};
13pub fn BitReader32(comptime ReaderType: type) type {
14 return BitReader(u32, ReaderType);
15}
1613
17/// Bit reader used during inflate (decompression). Has internal buffer of 6414/// Bit reader used during inflate (decompression). Has internal buffer of 64
18/// bits which shifts right after bits are consumed. Uses forward_reader to fill15/// bits which shifts right after bits are consumed. Uses forward_reader to fill
...@@ -23,14 +20,14 @@ pub fn BitReader32(comptime ReaderType: type) type {...@@ -23,14 +20,14 @@ pub fn BitReader32(comptime ReaderType: type) type {
23/// fill buffer from forward_reader by calling fill in advance and readF with20/// fill buffer from forward_reader by calling fill in advance and readF with
24/// buffered flag set.21/// buffered flag set.
25///22///
26pub fn BitReader(comptime T: type, comptime ReaderType: type) type {23pub fn BitReader(comptime T: type) type {
27 assert(T == u32 or T == u64);24 assert(T == u32 or T == u64);
28 const t_bytes: usize = @sizeOf(T);25 const t_bytes: usize = @sizeOf(T);
29 const Tshift = if (T == u64) u6 else u5;26 const Tshift = if (T == u64) u6 else u5;
3027
31 return struct {28 return struct {
32 // Underlying reader used for filling internal bits buffer29 // Underlying reader used for filling internal bits buffer
33 forward_reader: ReaderType = undefined,30 forward_reader: *std.io.BufferedReader,
34 // Internal buffer of 64 bits31 // Internal buffer of 64 bits
35 bits: T = 0,32 bits: T = 0,
36 // Number of bits in the buffer33 // Number of bits in the buffer
...@@ -38,10 +35,8 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {...@@ -38,10 +35,8 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
3835
39 const Self = @This();36 const Self = @This();
4037
41 pub const Error = ReaderType.Error || error{EndOfStream};38 pub fn init(forward_reader: *std.io.BufferedReader) Self {
4239 var self = Self{ .forward_reader = forward_reader };
43 pub fn init(rdr: ReaderType) Self {
44 var self = Self{ .forward_reader = rdr };
45 self.fill(1) catch {};40 self.fill(1) catch {};
46 return self;41 return self;
47 }42 }
...@@ -55,7 +50,7 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {...@@ -55,7 +50,7 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
55 /// bits to decode. So `nice` is not hard limit, it will just try to have50 /// bits to decode. So `nice` is not hard limit, it will just try to have
56 /// that number of bits available. If end of forward stream is reached51 /// that number of bits available. If end of forward stream is reached
57 /// it may be some extra zero bits in buffer.52 /// 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 {
59 if (self.nbits >= nice and nice != 0) {54 if (self.nbits >= nice and nice != 0) {
60 return; // We have enough bits55 return; // We have enough bits
61 }56 }
...@@ -86,31 +81,29 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {...@@ -86,31 +81,29 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
86 // First read from internal bits buffer.81 // First read from internal bits buffer.
87 var n: usize = 0;82 var n: usize = 0;
88 while (self.nbits > 0 and n < buf.len) {83 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 });
90 n += 1;85 n += 1;
91 }86 }
92 // Then use forward reader for all other bytes.87 // Then use forward reader for all other bytes.
93 try self.forward_reader.readNoEof(buf[n..]);88 try self.forward_reader.readNoEof(buf[n..]);
94 }89 }
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
102 /// Alias for readF(U, 0).91 /// Alias for readF(U, 0).
103 pub fn read(self: *Self, comptime U: type) !U {92 pub fn read(self: *Self, comptime U: type) !U {
104 return self.readF(U, 0);93 return self.readF(U, 0);
105 }94 }
10695
107 /// Alias for readF with flag.peak set.96 /// Alias for readF with flag.peak set.
108 pub inline fn peekF(self: *Self, comptime U: type, comptime how: u3) !U {97 pub inline fn peekF(self: *Self, comptime U: type, comptime how: Flags) !U {
109 return self.readF(U, how | flag.peek);98 return self.readF(U, .{
99 .peek = true,
100 .buffered = how.buffered,
101 .reverse = how.reverse,
102 });
110 }103 }
111104
112 /// Read with flags provided.105 /// 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 {
114 if (U == T) {107 if (U == T) {
115 assert(how == 0);108 assert(how == 0);
116 assert(self.alignBits() == 0);109 assert(self.alignBits() == 0);
...@@ -129,34 +122,35 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {...@@ -129,34 +122,35 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
129 try self.shift(n); // advance buffer for n122 try self.shift(n); // advance buffer for n
130 return u;123 return u;
131 },124 },
132 (flag.peek) => { // no shift, leave bits in the buffer125 .{ .peek = true } => { // no shift, leave bits in the buffer
133 try self.fill(n);126 try self.fill(n);
134 return @truncate(self.bits);127 return @truncate(self.bits);
135 },128 },
136 flag.buffered => { // no fill, assume that buffer has enough bits129 .{ .buffered = true } => { // no fill, assume that buffer has enough bits
137 const u: U = @truncate(self.bits);130 const u: U = @truncate(self.bits);
138 try self.shift(n);131 try self.shift(n);
139 return u;132 return u;
140 },133 },
141 (flag.reverse) => { // same as 0 with bit reverse134 .{ .reverse = true } => { // same as 0 with bit reverse
142 try self.fill(n);135 try self.fill(n);
143 const u: U = @truncate(self.bits);136 const u: U = @truncate(self.bits);
144 try self.shift(n);137 try self.shift(n);
145 return @bitReverse(u);138 return @bitReverse(u);
146 },139 },
147 (flag.peek | flag.reverse) => {140 .{ .peek = true, .reverse = true } => {
148 try self.fill(n);141 try self.fill(n);
149 return @bitReverse(@as(U, @truncate(self.bits)));142 return @bitReverse(@as(U, @truncate(self.bits)));
150 },143 },
151 (flag.buffered | flag.reverse) => {144 .{ .buffered = true, .reverse = true } => {
152 const u: U = @truncate(self.bits);145 const u: U = @truncate(self.bits);
153 try self.shift(n);146 try self.shift(n);
154 return @bitReverse(u);147 return @bitReverse(u);
155 },148 },
156 (flag.peek | flag.buffered) => {149 .{ .peek = true, .buffered = true },
150 => {
157 return @truncate(self.bits);151 return @truncate(self.bits);
158 },152 },
159 (flag.peek | flag.buffered | flag.reverse) => {153 .{ .peek = true, .buffered = true, .reverse = true } => {
160 return @bitReverse(@as(U, @truncate(self.bits)));154 return @bitReverse(@as(U, @truncate(self.bits)));
161 },155 },
162 }156 }
...@@ -169,7 +163,7 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {...@@ -169,7 +163,7 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
169 0 => {163 0 => {
170 try self.fill(n);164 try self.fill(n);
171 },165 },
172 flag.buffered => {},166 .{ .buffered = true } => {},
173 else => unreachable,167 else => unreachable,
174 }168 }
175 const mask: u16 = (@as(u16, 1) << n) - 1;169 const mask: u16 = (@as(u16, 1) << n) - 1;
...@@ -226,24 +220,24 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {...@@ -226,24 +220,24 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type {
226 /// 11000111220 /// 11000111
227 pub fn readFixedCode(self: *Self) !u16 {221 pub fn readFixedCode(self: *Self) !u16 {
228 try self.fill(7 + 2);222 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 });
230 if (code7 <= 0b0010_111) { // 7 bits, 256-279, codes 0000_000 - 0010_111224 if (code7 <= 0b0010_111) { // 7 bits, 256-279, codes 0000_000 - 0010_111
231 return @as(u16, code7) + 256;225 return @as(u16, code7) + 256;
232 } else if (code7 <= 0b1011_111) { // 8 bits, 0-143, codes 0011_0000 through 1011_1111226 } 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;
234 } else if (code7 <= 0b1100_011) { // 8 bit, 280-287, codes 1100_0000 - 1100_0111228 } 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;
236 } else { // 9 bit, 144-255, codes 1_1001_0000 - 1_1111_1111230 } 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;
238 }232 }
239 }233 }
240 };234 };
241}235}
242236
243test "readF" {237test "readF" {
244 var fbs = std.io.fixedBufferStream(&[_]u8{ 0xf3, 0x48, 0xcd, 0xc9, 0x00, 0x00 });238 var input: std.io.BufferedReader = undefined;
245 var br = bitReader(u64, fbs.reader());239 input.initFixed(&[_]u8{ 0xf3, 0x48, 0xcd, 0xc9, 0x00, 0x00 });
246 const F = BitReader64(@TypeOf(fbs.reader())).flag;240 var br: BitReader(u64) = .init(&input);
247241
248 try testing.expectEqual(@as(u8, 48), br.nbits);242 try testing.expectEqual(@as(u8, 48), br.nbits);
249 try testing.expectEqual(@as(u64, 0xc9cd48f3), br.bits);243 try testing.expectEqual(@as(u64, 0xc9cd48f3), br.bits);
...@@ -253,8 +247,8 @@ test "readF" {...@@ -253,8 +247,8 @@ test "readF" {
253 try testing.expectEqual(@as(u8, 48 - 3), br.nbits);247 try testing.expectEqual(@as(u8, 48 - 3), br.nbits);
254 try testing.expectEqual(@as(u3, 5), br.alignBits());248 try testing.expectEqual(@as(u3, 5), br.alignBits());
255249
256 try testing.expect(try br.readF(u8, F.peek) == 0b0001_1110);250 try testing.expect(try br.readF(u8, .{ .peek = true }) == 0b0001_1110);
257 try testing.expect(try br.readF(u9, F.peek) == 0b1_0001_1110);251 try testing.expect(try br.readF(u9, .{ .peek = true }) == 0b1_0001_1110);
258 try br.shift(9);252 try br.shift(9);
259 try testing.expectEqual(@as(u8, 36), br.nbits);253 try testing.expectEqual(@as(u8, 36), br.nbits);
260 try testing.expectEqual(@as(u3, 4), br.alignBits());254 try testing.expectEqual(@as(u3, 4), br.alignBits());
...@@ -283,15 +277,15 @@ test "read block type 1 data" {...@@ -283,15 +277,15 @@ test "read block type 1 data" {
283 0x0c, 0x01, 0x02, 0x03, //277 0x0c, 0x01, 0x02, 0x03, //
284 0xaa, 0xbb, 0xcc, 0xdd,278 0xaa, 0xbb, 0xcc, 0xdd,
285 };279 };
286 var fbs = std.io.fixedBufferStream(&data);280 var fbs: std.io.BufferedReader = undefined;
287 var br = bitReader(T, fbs.reader());281 fbs.initFixed(&data);
288 const F = BitReader(T, @TypeOf(fbs.reader())).flag;282 var br: BitReader(T) = .init(&fbs);
289283
290 try testing.expectEqual(@as(u1, 1), try br.readF(u1, 0)); // bfinal284 try testing.expectEqual(@as(u1, 1), try br.readF(u1, 0)); // bfinal
291 try testing.expectEqual(@as(u2, 1), try br.readF(u2, 0)); // block_type285 try testing.expectEqual(@as(u2, 1), try br.readF(u2, 0)); // block_type
292286
293 for ("Hello world\n") |c| {287 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);
295 }289 }
296 try testing.expectEqual(@as(u7, 0), try br.readF(u7, 0)); // end of block290 try testing.expectEqual(@as(u7, 0), try br.readF(u7, 0)); // end of block
297 br.alignToByte();291 br.alignToByte();
...@@ -306,8 +300,9 @@ test "shift/fill" {...@@ -306,8 +300,9 @@ test "shift/fill" {
306 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,300 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
307 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,301 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
308 };302 };
309 var fbs = std.io.fixedBufferStream(&data);303 var fbs: std.io.BufferedReader = undefined;
310 var br = bitReader(u64, fbs.reader());304 fbs.initFixed(&data);
305 var br: BitReader(u64) = .init(&fbs);
311306
312 try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits);307 try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits);
313 try br.shift(8);308 try br.shift(8);
...@@ -332,8 +327,9 @@ test "readAll" {...@@ -332,8 +327,9 @@ test "readAll" {
332 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,327 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
333 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,328 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
334 };329 };
335 var fbs = std.io.fixedBufferStream(&data);330 var fbs: std.io.BufferedReader = undefined;
336 var br = bitReader(T, fbs.reader());331 fbs.initFixed(&data);
332 var br: BitReader(T) = .init(&fbs);
337333
338 switch (T) {334 switch (T) {
339 u64 => try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits),335 u64 => try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits),
...@@ -354,8 +350,9 @@ test "readFixedCode" {...@@ -354,8 +350,9 @@ test "readFixedCode" {
354 inline for ([_]type{ u64, u32 }) |T| {350 inline for ([_]type{ u64, u32 }) |T| {
355 const fixed_codes = @import("huffman_encoder.zig").fixed_codes;351 const fixed_codes = @import("huffman_encoder.zig").fixed_codes;
356352
357 var fbs = std.io.fixedBufferStream(&fixed_codes);353 var fbs: std.io.BufferedReader = undefined;
358 var rdr = bitReader(T, fbs.reader());354 fbs.initFixed(&fixed_codes);
355 var rdr: BitReader(T) = .init(&fbs);
359356
360 for (0..286) |c| {357 for (0..286) |c| {
361 try testing.expectEqual(c, try rdr.readFixedCode());358 try testing.expectEqual(c, try rdr.readFixedCode());
...@@ -369,8 +366,9 @@ test "u32 leaves no bits on u32 reads" {...@@ -369,8 +366,9 @@ test "u32 leaves no bits on u32 reads" {
369 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,366 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
370 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,367 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
371 };368 };
372 var fbs = std.io.fixedBufferStream(&data);369 var fbs: std.io.BufferedReader = undefined;
373 var br = bitReader(u32, fbs.reader());370 fbs.initFixed(&data);
371 var br: BitReader(u32) = .init(&fbs);
374372
375 _ = try br.read(u3);373 _ = try br.read(u3);
376 try testing.expectEqual(29, br.nbits);374 try testing.expectEqual(29, br.nbits);
...@@ -396,8 +394,9 @@ test "u64 need fill after alignToByte" {...@@ -396,8 +394,9 @@ test "u64 need fill after alignToByte" {
396 };394 };
397395
398 // without fill396 // without fill
399 var fbs = std.io.fixedBufferStream(&data);397 var fbs: std.io.BufferedReader = undefined;
400 var br = bitReader(u64, fbs.reader());398 fbs.initFixed(&data);
399 var br: BitReader(u64) = .init(&fbs);
401 _ = try br.read(u23);400 _ = try br.read(u23);
402 try testing.expectEqual(41, br.nbits);401 try testing.expectEqual(41, br.nbits);
403 br.alignToByte();402 br.alignToByte();
...@@ -409,7 +408,7 @@ test "u64 need fill after alignToByte" {...@@ -409,7 +408,7 @@ test "u64 need fill after alignToByte" {
409408
410 // fill after align ensures all bits filled409 // fill after align ensures all bits filled
411 fbs.reset();410 fbs.reset();
412 br = bitReader(u64, fbs.reader());411 br = .init(&fbs);
413 _ = try br.read(u23);412 _ = try br.read(u23);
414 try testing.expectEqual(41, br.nbits);413 try testing.expectEqual(41, br.nbits);
415 br.alignToByte();414 br.alignToByte();
lib/std/compress/lzma2.zig+4-8
...@@ -3,11 +3,7 @@ const Allocator = std.mem.Allocator;...@@ -3,11 +3,7 @@ const Allocator = std.mem.Allocator;
33
4pub const decode = @import("lzma2/decode.zig");4pub const decode = @import("lzma2/decode.zig");
55
6pub fn decompress(6pub fn decompress(allocator: Allocator, reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) !void {
7 allocator: Allocator,
8 reader: anytype,
9 writer: anytype,
10) !void {
11 var decoder = try decode.Decoder.init(allocator);7 var decoder = try decode.Decoder.init(allocator);
12 defer decoder.deinit(allocator);8 defer decoder.deinit(allocator);
13 return decoder.decompress(allocator, reader, writer);9 return decoder.decompress(allocator, reader, writer);
...@@ -19,11 +15,11 @@ test {...@@ -19,11 +15,11 @@ test {
19 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02,15 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02,
20 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00,16 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00,
21 };17 };
22 var stream: std.io.FixedBufferStream = .{ .buffer = compressed };18 var stream: std.io.BufferedReader = undefined;
2319 stream.initFixed(&compressed);
24 var decomp: std.io.AllocatingWriter = undefined;20 var decomp: std.io.AllocatingWriter = undefined;
25 const decomp_bw = decomp.init(std.testing.allocator);21 const decomp_bw = decomp.init(std.testing.allocator);
26 defer decomp.deinit();22 defer decomp.deinit();
27 try decompress(std.testing.allocator, stream.reader(), decomp_bw);23 try decompress(std.testing.allocator, &stream, decomp_bw);
28 try std.testing.expectEqualSlices(u8, expected, decomp.getWritten());24 try std.testing.expectEqualSlices(u8, expected, decomp.getWritten());
29}25}
lib/std/debug/Dwarf.zig+6-5
...@@ -2235,14 +2235,15 @@ pub const ElfModule = struct {...@@ -2235,14 +2235,15 @@ pub const ElfModule = struct {
22352235
2236 const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);2236 const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
2237 sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {2237 sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {
2238 var section_stream: std.io.FixedBufferStream = .{ .buffer = section_bytes };2238 var section_reader: std.io.BufferedReader = undefined;
2239 const section_reader = section_stream.reader();2239 section_reader.initFixed(section_bytes);
2240 const chdr = section_reader.readStruct(elf.Chdr) catch continue;2240 const chdr = section_reader.takeStruct(elf.Chdr) catch continue;
2241 if (chdr.ch_type != .ZLIB) continue;2241 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);
2246 errdefer gpa.free(decompressed_section);2247 errdefer gpa.free(decompressed_section);
22472248
2248 const read = zlib_stream.reader().readAll(decompressed_section) catch continue;2249 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 {...@@ -1590,8 +1590,10 @@ pub fn reader(file: File) std.io.Reader {
1590 return .{1590 return .{
1591 .context = handleToOpaque(file.handle),1591 .context = handleToOpaque(file.handle),
1592 .vtable = .{1592 .vtable = .{
1593 .seekRead = reader_seekRead,1593 .posRead = reader_posRead,
1594 .posReadVec = reader_posReadVec,
1594 .streamRead = reader_streamRead,1595 .streamRead = reader_streamRead,
1596 .streamReadVec = reader_streamReadVec,
1595 },1597 },
1596 };1598 };
1597}1599}
...@@ -1610,7 +1612,7 @@ pub fn writer(file: File) std.io.Writer {...@@ -1610,7 +1612,7 @@ pub fn writer(file: File) std.io.Writer {
1610/// vectors through the underlying write calls as possible.1612/// vectors through the underlying write calls as possible.
1611const max_buffers_len = 16;1613const max_buffers_len = 16;
16121614
1613pub fn reader_seekRead(1615pub fn reader_posRead(
1614 context: *anyopaque,1616 context: *anyopaque,
1615 bw: *std.io.BufferedWriter,1617 bw: *std.io.BufferedWriter,
1616 limit: std.io.Reader.Limit,1618 limit: std.io.Reader.Limit,
...@@ -1621,10 +1623,36 @@ pub fn reader_seekRead(...@@ -1621,10 +1623,36 @@ pub fn reader_seekRead(
1621 return writer.writeFile(bw, file, .init(offset), len, &.{}, 0);1623 return writer.writeFile(bw, file, .init(offset), len, &.{}, 0);
1622}1624}
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 {
1625 const file = opaqueToHandle(context);1640 const file = opaqueToHandle(context);
1626 const len: std.io.Writer.Len = if (limit.unwrap()) |l| .init(l) else .entire_file;1641 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 };
1628}1656}
16291657
1630pub fn writer_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {1658pub fn writer_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
...@@ -1671,7 +1699,7 @@ pub fn writer_writeFile(...@@ -1671,7 +1699,7 @@ pub fn writer_writeFile(
1671 context: *anyopaque,1699 context: *anyopaque,
1672 in_file: std.fs.File,1700 in_file: std.fs.File,
1673 in_offset: u64,1701 in_offset: u64,
1674 in_len: std.io.Writer.VTable.FileLen,1702 in_len: std.io.Writer.FileLen,
1675 headers_and_trailers: []const []const u8,1703 headers_and_trailers: []const []const u8,
1676 headers_len: usize,1704 headers_len: usize,
1677) anyerror!usize {1705) anyerror!usize {
...@@ -1705,7 +1733,7 @@ fn writeFileUnseekable(...@@ -1705,7 +1733,7 @@ fn writeFileUnseekable(
1705 out_fd: Handle,1733 out_fd: Handle,
1706 in_fd: Handle,1734 in_fd: Handle,
1707 in_offset: u64,1735 in_offset: u64,
1708 in_len: std.io.Writer.VTable.FileLen,1736 in_len: std.io.Writer.FileLen,
1709 headers_and_trailers: []const []const u8,1737 headers_and_trailers: []const []const u8,
1710 headers_len: usize,1738 headers_len: usize,
1711) anyerror!usize {1739) anyerror!usize {
lib/std/http/Client.zig+2-2
...@@ -1119,7 +1119,7 @@ pub const Request = struct {...@@ -1119,7 +1119,7 @@ pub const Request = struct {
1119 context: *anyopaque,1119 context: *anyopaque,
1120 file: std.fs.File,1120 file: std.fs.File,
1121 offset: u64,1121 offset: u64,
1122 len: std.io.Writer.VTable.FileLen,1122 len: std.io.Writer.FileLen,
1123 headers_and_trailers: []const []const u8,1123 headers_and_trailers: []const []const u8,
1124 headers_len: usize,1124 headers_len: usize,
1125 ) anyerror!usize {1125 ) anyerror!usize {
...@@ -1159,7 +1159,7 @@ pub const Request = struct {...@@ -1159,7 +1159,7 @@ pub const Request = struct {
1159 context: *anyopaque,1159 context: *anyopaque,
1160 file: std.fs.File,1160 file: std.fs.File,
1161 offset: u64,1161 offset: u64,
1162 len: std.io.Writer.VTable.FileLen,1162 len: std.io.Writer.FileLen,
1163 headers_and_trailers: []const []const u8,1163 headers_and_trailers: []const []const u8,
1164 headers_len: usize,1164 headers_len: usize,
1165 ) anyerror!usize {1165 ) anyerror!usize {
lib/std/http/Server.zig+2-2
...@@ -881,7 +881,7 @@ pub const Response = struct {...@@ -881,7 +881,7 @@ pub const Response = struct {
881 context: *anyopaque,881 context: *anyopaque,
882 file: std.fs.File,882 file: std.fs.File,
883 offset: u64,883 offset: u64,
884 len: std.io.Writer.VTable.FileLen,884 len: std.io.Writer.FileLen,
885 headers_and_trailers: []const []const u8,885 headers_and_trailers: []const []const u8,
886 headers_len: usize,886 headers_len: usize,
887 ) anyerror!usize {887 ) anyerror!usize {
...@@ -948,7 +948,7 @@ pub const Response = struct {...@@ -948,7 +948,7 @@ pub const Response = struct {
948 context: *anyopaque,948 context: *anyopaque,
949 file: std.fs.File,949 file: std.fs.File,
950 offset: u64,950 offset: u64,
951 len: std.io.Writer.VTable.FileLen,951 len: std.io.Writer.FileLen,
952 headers_and_trailers: []const []const u8,952 headers_and_trailers: []const []const u8,
953 headers_len: usize,953 headers_len: usize,
954 ) anyerror!usize {954 ) anyerror!usize {
lib/std/io.zig+1-1
...@@ -118,7 +118,7 @@ fn null_writeFile(...@@ -118,7 +118,7 @@ fn null_writeFile(
118 context: *anyopaque,118 context: *anyopaque,
119 file: std.fs.File,119 file: std.fs.File,
120 offset: u64,120 offset: u64,
121 len: Writer.VTable.FileLen,121 len: Writer.FileLen,
122 headers_and_trailers: []const []const u8,122 headers_and_trailers: []const []const u8,
123 headers_len: usize,123 headers_len: usize,
124) anyerror!usize {124) anyerror!usize {
lib/std/io/AllocatingWriter.zig+1-1
...@@ -153,7 +153,7 @@ fn writeFile(...@@ -153,7 +153,7 @@ fn writeFile(
153 context: *anyopaque,153 context: *anyopaque,
154 file: std.fs.File,154 file: std.fs.File,
155 offset: u64,155 offset: u64,
156 len: std.io.Writer.VTable.FileLen,156 len: std.io.Writer.FileLen,
157 headers_and_trailers_full: []const []const u8,157 headers_and_trailers_full: []const []const u8,
158 headers_len_full: usize,158 headers_len_full: usize,
159) anyerror!usize {159) anyerror!usize {
lib/std/io/BufferedReader.zig+21-4
...@@ -25,7 +25,7 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {...@@ -25,7 +25,7 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
25 .context = br,25 .context = br,
26 .vtable = &.{26 .vtable = &.{
27 .streamRead = null,27 .streamRead = null,
28 .seekRead = null,28 .posRead = null,
29 },29 },
30 },30 },
31 };31 };
...@@ -44,7 +44,9 @@ pub fn reader(br: *BufferedReader) Reader {...@@ -44,7 +44,9 @@ pub fn reader(br: *BufferedReader) Reader {
44 .context = br,44 .context = br,
45 .vtable = &.{45 .vtable = &.{
46 .streamRead = passthru_streamRead,46 .streamRead = passthru_streamRead,
47 .seekRead = passthru_seekRead,47 .streamReadVec = passthru_streamReadVec,
48 .posRead = passthru_posRead,
49 .posReadVec = passthru_posReadVec,
48 },50 },
49 };51 };
50}52}
...@@ -65,14 +67,29 @@ fn passthru_streamRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit...@@ -65,14 +67,29 @@ fn passthru_streamRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit
65 return br.unbuffered_reader.streamRead(bw, limit);67 return br.unbuffered_reader.streamRead(bw, limit);
66}68}
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 {
69 const br: *BufferedReader = @alignCast(@ptrCast(ctx));78 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
70 const buffer = br.storage.buffer.items;79 const buffer = br.storage.buffer.items;
71 if (off < buffer.len) {80 if (off < buffer.len) {
72 const send = buffer[off..limit.min(buffer.len)];81 const send = buffer[off..limit.min(buffer.len)];
73 return bw.writeSplat(send, 1);82 return bw.writeSplat(send, 1);
74 }83 }
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");
76}93}
7794
78/// Returns the next `n` bytes from `unbuffered_reader`, filling the buffer as95/// 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;...@@ -10,30 +10,15 @@ const testing = std.testing;
10///10///
11/// If this has capacity zero, the writer is unbuffered, and `flush` is a no-op.11/// If this has capacity zero, the writer is unbuffered, and `flush` is a no-op.
12buffer: std.ArrayListUnmanaged(u8),12buffer: std.ArrayListUnmanaged(u8),
13mode: union(enum) {13/// Underlying stream to send bytes to.
14 /// Return `error.NoSpaceLeft` if a write could not fit into the buffer.14///
15 fixed,15/// A write will only be sent here if it could not fit into `buffer`, or if it
16 /// Underlying stream to send bytes to.16/// is a `writeFile`.
17 ///17///
18 /// A write will only be sent here if it could not fit into `buffer`, or if18/// `unbuffered_writer` may modify `buffer` if the number of bytes returned
19 /// it is a `writeFile`.19/// equals number of bytes provided. This property is exploited by
20 ///20/// `std.io.AllocatingWriter` for example.
21 /// `unbuffered_writer` may modify `buffer` if the number of bytes returned21unbuffered_writer: Writer,
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}
3722
38/// Number of slices to store on the stack, when trying to send as many byte23/// Number of slices to store on the stack, when trying to send as many byte
39/// vectors through the underlying write calls as possible.24/// vectors through the underlying write calls as possible.
...@@ -72,23 +57,24 @@ pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {...@@ -72,23 +57,24 @@ pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void {
72/// This function is available when using `initFixed`.57/// This function is available when using `initFixed`.
73pub fn getWritten(bw: *const BufferedWriter) []u8 {58pub fn getWritten(bw: *const BufferedWriter) []u8 {
74 assert(bw.unbuffered_writer.vtable == &fixed_vtable);59 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
75 return bw.buffer[0..bw.end];60 return bw.buffer.items;
76}61}
7762
78/// This function is available when using `initFixed`.63/// This function is available when using `initFixed`.
79pub fn reset(bw: *BufferedWriter) void {64pub fn reset(bw: *BufferedWriter) void {
80 assert(bw.unbuffered_writer.vtable == &fixed_vtable);65 assert(bw.unbuffered_writer.vtable == &fixed_vtable);
81 bw.end = 0;66 bw.buffer.items.len = 0;
82}67}
8368
84pub fn flush(bw: *BufferedWriter) anyerror!void {69pub 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;
86 try bw.unbuffered_writer.writeAll(send_buffer);72 try bw.unbuffered_writer.writeAll(send_buffer);
87 bw.end = 0;73 list.items.len = 0;
88}74}
8975
90pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {76pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 {
91 return bw.buffer[bw.end..];77 return bw.buffer.unusedCapacitySlice();
92}78}
9379
94/// The `data` parameter is mutable because this function needs to mutate the80/// 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 {...@@ -116,11 +102,12 @@ pub fn writev(bw: *BufferedWriter, data: []const []const u8) anyerror!usize {
116102
117fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {103fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
118 const bw: *BufferedWriter = @alignCast(@ptrCast(context));104 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
119 const buffer = bw.buffer;105 const list = &bw.buffer;
120 const start_end = bw.end;106 const buffer = list.allocatedSlice();
107 const start_end = list.items.len;
121108
122 var buffers: [max_buffers_len][]const u8 = undefined;109 var buffers: [max_buffers_len][]const u8 = undefined;
123 var end = bw.end;110 var end = start_end;
124 for (data, 0..) |bytes, i| {111 for (data, 0..) |bytes, i| {
125 const new_end = end + bytes.len;112 const new_end = end + bytes.len;
126 if (new_end <= buffer.len) {113 if (new_end <= buffer.len) {
...@@ -144,10 +131,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -144,10 +131,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
144 @branchHint(.unlikely);131 @branchHint(.unlikely);
145 const remainder = buffer[n..end];132 const remainder = buffer[n..end];
146 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);133 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
147 bw.end = remainder.len;134 list.items.len = remainder.len;
148 return end - start_end;135 return end - start_end;
149 }136 }
150 bw.end = 0;137 list.items.len = 0;
151 return n - start_end;138 return n - start_end;
152 }139 }
153 const n = try bw.unbuffered_writer.writeSplat(send_buffers, 1);140 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...@@ -155,10 +142,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
155 @branchHint(.unlikely);142 @branchHint(.unlikely);
156 const remainder = buffer[n..end];143 const remainder = buffer[n..end];
157 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);144 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
158 bw.end = remainder.len;145 list.items.len = remainder.len;
159 return end - start_end;146 return end - start_end;
160 }147 }
161 bw.end = 0;148 list.items.len = 0;
162 return n - start_end;149 return n - start_end;
163 }150 }
164151
...@@ -168,7 +155,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -168,7 +155,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
168 @branchHint(.unlikely);155 @branchHint(.unlikely);
169 // It was added in the loop above; undo it here.156 // It was added in the loop above; undo it here.
170 end -= pattern.len;157 end -= pattern.len;
171 bw.end = end;158 list.items.len = end;
172 return end - start_end;159 return end - start_end;
173 }160 }
174161
...@@ -176,7 +163,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -176,7 +163,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
176163
177 switch (pattern.len) {164 switch (pattern.len) {
178 0 => {165 0 => {
179 bw.end = end;166 list.items.len = end;
180 return end - start_end;167 return end - start_end;
181 },168 },
182 1 => {169 1 => {
...@@ -184,7 +171,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -184,7 +171,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
184 if (new_end <= buffer.len) {171 if (new_end <= buffer.len) {
185 @branchHint(.likely);172 @branchHint(.likely);
186 @memset(buffer[end..new_end], pattern[0]);173 @memset(buffer[end..new_end], pattern[0]);
187 bw.end = new_end;174 list.items.len = new_end;
188 return new_end - start_end;175 return new_end - start_end;
189 }176 }
190 buffers[0] = buffer[0..end];177 buffers[0] = buffer[0..end];
...@@ -194,10 +181,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -194,10 +181,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
194 @branchHint(.unlikely);181 @branchHint(.unlikely);
195 const remainder = buffer[n..end];182 const remainder = buffer[n..end];
196 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);183 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
197 bw.end = remainder.len;184 list.items.len = remainder.len;
198 return end - start_end;185 return end - start_end;
199 }186 }
200 bw.end = 0;187 list.items.len = 0;
201 return n - start_end;188 return n - start_end;
202 },189 },
203 else => {190 else => {
...@@ -207,7 +194,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -207,7 +194,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
207 while (end < new_end) : (end += pattern.len) {194 while (end < new_end) : (end += pattern.len) {
208 @memcpy(buffer[end..][0..pattern.len], pattern);195 @memcpy(buffer[end..][0..pattern.len], pattern);
209 }196 }
210 bw.end = new_end;197 list.items.len = new_end;
211 return new_end - start_end;198 return new_end - start_end;
212 }199 }
213 buffers[0] = buffer[0..end];200 buffers[0] = buffer[0..end];
...@@ -217,10 +204,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -217,10 +204,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
217 @branchHint(.unlikely);204 @branchHint(.unlikely);
218 const remainder = buffer[n..end];205 const remainder = buffer[n..end];
219 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);206 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
220 bw.end = remainder.len;207 list.items.len = remainder.len;
221 return end - start_end;208 return end - start_end;
222 }209 }
223 bw.end = 0;210 list.items.len = 0;
224 return n - start_end;211 return n - start_end;
225 },212 },
226 }213 }
...@@ -228,12 +215,14 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi...@@ -228,12 +215,14 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi
228215
229fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize {216fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize {
230 const bw: *BufferedWriter = @alignCast(@ptrCast(context));217 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
218 const list = &bw.buffer;
231 // When this function is called it means the buffer got full, so it's time219 // When this function is called it means the buffer got full, so it's time
232 // to return an error. However, we still need to make sure all of the220 // to return an error. However, we still need to make sure all of the
233 // available buffer has been used.221 // available buffer has been used.
234 const first = data[0];222 const first = data[0];
235 const dest = bw.buffer[bw.end..];223 const dest = list.unusedCapacitySlice();
236 @memcpy(dest, first[0..dest.len]);224 @memcpy(dest, first[0..dest.len]);
225 list.items.len = list.capacity;
237 return error.NoSpaceLeft;226 return error.NoSpaceLeft;
238}227}
239228
...@@ -242,26 +231,29 @@ fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize {...@@ -242,26 +231,29 @@ fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize {
242/// available buffer has been filled.231/// available buffer has been filled.
243fn fixed_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {232fn fixed_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
244 const bw: *BufferedWriter = @alignCast(@ptrCast(context));233 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
234 const list = &bw.buffer;
245 for (data) |bytes| {235 for (data) |bytes| {
246 const dest = bw.buffer[bw.end..];236 const dest = list.unusedCapacitySlice();
247 if (dest.len == 0) return error.NoSpaceLeft;237 if (dest.len == 0) return error.NoSpaceLeft;
248 const len = @min(bytes.len, dest.len);238 const len = @min(bytes.len, dest.len);
249 @memcpy(dest[0..len], bytes[0..len]);239 @memcpy(dest[0..len], bytes[0..len]);
250 bw.end += len;240 list.items.len += len;
251 }241 }
252 const pattern = data[data.len - 1];242 const pattern = data[data.len - 1];
253 const dest = bw.buffer[bw.end..];243 const dest = list.unusedCapacitySlice();
254 switch (pattern.len) {244 switch (pattern.len) {
255 0 => unreachable,245 0 => unreachable,
256 1 => @memset(dest, pattern[0]),246 1 => @memset(dest, pattern[0]),
257 else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern),247 else => for (0..splat - 1) |i| @memcpy(dest[i * pattern.len ..][0..pattern.len], pattern),
258 }248 }
249 list.items.len = list.capacity;
259 return error.NoSpaceLeft;250 return error.NoSpaceLeft;
260}251}
261252
262pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {253pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
263 const buffer = bw.buffer;254 const list = &bw.buffer;
264 const end = bw.end;255 const buffer = list.allocatedSlice();
256 const end = list.items.len;
265 const new_end = end + bytes.len;257 const new_end = end + bytes.len;
266 if (new_end > buffer.len) {258 if (new_end > buffer.len) {
267 var data: [2][]const u8 = .{ buffer[0..end], bytes };259 var data: [2][]const u8 = .{ buffer[0..end], bytes };
...@@ -270,14 +262,14 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {...@@ -270,14 +262,14 @@ pub fn write(bw: *BufferedWriter, bytes: []const u8) anyerror!usize {
270 @branchHint(.unlikely);262 @branchHint(.unlikely);
271 const remainder = buffer[n..end];263 const remainder = buffer[n..end];
272 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);264 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
273 bw.end = remainder.len;265 list.items.len = remainder.len;
274 return 0;266 return 0;
275 }267 }
276 bw.end = 0;268 list.items.len = 0;
277 return n - end;269 return n - end;
278 }270 }
279 @memcpy(buffer[end..new_end], bytes);271 @memcpy(buffer[end..new_end], bytes);
280 bw.end = new_end;272 list.items.len = new_end;
281 return bytes.len;273 return bytes.len;
282}274}
283275
...@@ -302,35 +294,29 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {...@@ -302,35 +294,29 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
302 list.items.len = buffer.len + 1;294 list.items.len = buffer.len + 1;
303 return;295 return;
304 }296 }
305 switch (bw.mode) {297 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
306 .fixed => return error.NoSpaceLeft,298 while (true) {
307 .writer => |w| {299 const n = try bw.unbuffered_writer.writev(&buffers);
308 var buffers: [2][]const u8 = .{ buffer, &.{byte} };300 if (n == 0) {
309 while (true) {301 @branchHint(.unlikely);
310 const n = try w.writev(&buffers);302 continue;
311 if (n == 0) {303 } else if (n >= buffer.len) {
312 @branchHint(.unlikely);304 @branchHint(.likely);
313 continue;305 if (n > buffer.len) {
314 } else if (n >= buffer.len) {306 @branchHint(.likely);
315 @branchHint(.likely);307 list.items.len = 0;
316 if (n > buffer.len) {308 return;
317 @branchHint(.likely);309 } else {
318 list.items.len = 0;310 buffer[0] = byte;
319 return;311 list.items.len = 1;
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;
330 return;312 return;
331 }313 }
332 },314 }
333 .allocator => |gpa| try list.append(gpa, byte),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;
334 }320 }
335}321}
336322
...@@ -395,7 +381,7 @@ pub fn writeFile(...@@ -395,7 +381,7 @@ pub fn writeFile(
395 bw: *BufferedWriter,381 bw: *BufferedWriter,
396 file: std.fs.File,382 file: std.fs.File,
397 offset: u64,383 offset: u64,
398 len: Writer.VTable.FileLen,384 len: Writer.FileLen,
399 headers_and_trailers: []const []const u8,385 headers_and_trailers: []const []const u8,
400 headers_len: usize,386 headers_len: usize,
401) anyerror!usize {387) anyerror!usize {
...@@ -406,14 +392,15 @@ fn passthru_writeFile(...@@ -406,14 +392,15 @@ fn passthru_writeFile(
406 context: *anyopaque,392 context: *anyopaque,
407 file: std.fs.File,393 file: std.fs.File,
408 offset: u64,394 offset: u64,
409 len: Writer.VTable.FileLen,395 len: Writer.FileLen,
410 headers_and_trailers: []const []const u8,396 headers_and_trailers: []const []const u8,
411 headers_len: usize,397 headers_len: usize,
412) anyerror!usize {398) anyerror!usize {
413 const bw: *BufferedWriter = @alignCast(@ptrCast(context));399 const bw: *BufferedWriter = @alignCast(@ptrCast(context));
414 const buffer = bw.buffer;400 const list = &bw.buffer;
401 const buffer = list.allocatedSlice();
415 if (buffer.len == 0) return bw.unbuffered_writer.writeFile(file, offset, len, headers_and_trailers, headers_len);402 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;
417 const headers = headers_and_trailers[0..headers_len];404 const headers = headers_and_trailers[0..headers_len];
418 const trailers = headers_and_trailers[headers_len..];405 const trailers = headers_and_trailers[headers_len..];
419 var buffers: [max_buffers_len][]const u8 = undefined;406 var buffers: [max_buffers_len][]const u8 = undefined;
...@@ -443,10 +430,10 @@ fn passthru_writeFile(...@@ -443,10 +430,10 @@ fn passthru_writeFile(
443 @branchHint(.unlikely);430 @branchHint(.unlikely);
444 const remainder = buffer[n..end];431 const remainder = buffer[n..end];
445 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);432 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
446 bw.end = remainder.len;433 list.items.len = remainder.len;
447 return end - start_end;434 return end - start_end;
448 }435 }
449 bw.end = 0;436 list.items.len = 0;
450 return n - start_end;437 return n - start_end;
451 }438 }
452 // Have not made it past the headers yet; must call `writev`.439 // Have not made it past the headers yet; must call `writev`.
...@@ -455,10 +442,10 @@ fn passthru_writeFile(...@@ -455,10 +442,10 @@ fn passthru_writeFile(
455 @branchHint(.unlikely);442 @branchHint(.unlikely);
456 const remainder = buffer[n..end];443 const remainder = buffer[n..end];
457 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);444 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
458 bw.end = remainder.len;445 list.items.len = remainder.len;
459 return end - start_end;446 return end - start_end;
460 }447 }
461 bw.end = 0;448 list.items.len = 0;
462 return n - start_end;449 return n - start_end;
463 }450 }
464 // All headers written to buffer.451 // All headers written to buffer.
...@@ -473,10 +460,10 @@ fn passthru_writeFile(...@@ -473,10 +460,10 @@ fn passthru_writeFile(
473 @branchHint(.unlikely);460 @branchHint(.unlikely);
474 const remainder = buffer[n..end];461 const remainder = buffer[n..end];
475 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);462 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
476 bw.end = remainder.len;463 list.items.len = remainder.len;
477 return end - start_end;464 return end - start_end;
478 }465 }
479 bw.end = 0;466 list.items.len = 0;
480 return n - start_end;467 return n - start_end;
481}468}
482469
...@@ -484,7 +471,7 @@ pub const WriteFileOptions = struct {...@@ -484,7 +471,7 @@ pub const WriteFileOptions = struct {
484 offset: u64 = 0,471 offset: u64 = 0,
485 /// If the size of the source file is known, it is likely that passing the472 /// If the size of the source file is known, it is likely that passing the
486 /// size here will save one syscall.473 /// size here will save one syscall.
487 len: Writer.VTable.FileLen = .entire_file,474 len: Writer.FileLen = .entire_file,
488 /// Headers and trailers must be passed together so that in case `len` is475 /// Headers and trailers must be passed together so that in case `len` is
489 /// zero, they can be forwarded directly to `Writer.VTable.writev`.476 /// zero, they can be forwarded directly to `Writer.VTable.writev`.
490 ///477 ///
lib/std/io/CountingWriter.zig+1-1
...@@ -33,7 +33,7 @@ fn passthru_writeFile(...@@ -33,7 +33,7 @@ fn passthru_writeFile(
33 context: *anyopaque,33 context: *anyopaque,
34 file: std.fs.File,34 file: std.fs.File,
35 offset: u64,35 offset: u64,
36 len: Writer.VTable.FileLen,36 len: Writer.FileLen,
37 headers_and_trailers: []const []const u8,37 headers_and_trailers: []const []const u8,
38 headers_len: usize,38 headers_len: usize,
39) anyerror!usize {39) anyerror!usize {
lib/std/io/Reader.zig+13-11
...@@ -19,10 +19,11 @@ pub const VTable = struct {...@@ -19,10 +19,11 @@ pub const VTable = struct {
19 ///19 ///
20 /// If this is `null` it is equivalent to always returning20 /// If this is `null` it is equivalent to always returning
21 /// `error.Unseekable`.21 /// `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
24 /// Writes bytes from the internally tracked stream position to `bw`, or25 /// Writes bytes from the internally tracked stream position to `bw`, or
25 /// returns `error.Unstreamable`, indicating `seekRead` should be used26 /// returns `error.Unstreamable`, indicating `posRead` should be used
26 /// instead.27 /// instead.
27 ///28 ///
28 /// Returns the number of bytes written, which will be at minimum `0` and at29 /// Returns the number of bytes written, which will be at minimum `0` and at
...@@ -37,9 +38,10 @@ pub const VTable = struct {...@@ -37,9 +38,10 @@ pub const VTable = struct {
37 /// If this is `null` it is equivalent to always returning38 /// If this is `null` it is equivalent to always returning
38 /// `error.Unstreamable`.39 /// `error.Unstreamable`.
39 streamRead: ?*const fn (ctx: *anyopaque, bw: *std.io.BufferedWriter, limit: Limit) anyerror!Status,40 streamRead: ?*const fn (ctx: *anyopaque, bw: *std.io.BufferedWriter, limit: Limit) anyerror!Status,
41 streamReadVec: ?*const fn (ctx: *anyopaque, data: []const []u8) anyerror!Status,
40};42};
4143
42pub const Len = @Type(.{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 });44pub const Len = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } });
4345
44pub const Status = packed struct(usize) {46pub const Status = packed struct(usize) {
45 /// Number of bytes that were written to `writer`.47 /// Number of bytes that were written to `writer`.
...@@ -56,7 +58,7 @@ pub const Limit = enum(usize) {...@@ -56,7 +58,7 @@ pub const Limit = enum(usize) {
56/// Returns total number of bytes written to `w`.58/// Returns total number of bytes written to `w`.
57pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {59pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {
58 if (r.vtable.pread != null) {60 if (r.vtable.pread != null) {
59 return seekReadAll(r, w) catch |err| switch (err) {61 return posReadAll(r, w) catch |err| switch (err) {
60 error.Unseekable => {},62 error.Unseekable => {},
61 else => return err,63 else => return err,
62 };64 };
...@@ -68,11 +70,11 @@ pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {...@@ -68,11 +70,11 @@ pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {
68///70///
69/// May return `error.Unseekable`, indicating this function cannot be used to71/// May return `error.Unseekable`, indicating this function cannot be used to
70/// read from the reader.72/// read from the reader.
71pub fn seekReadAll(r: Reader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize {73pub fn posReadAll(r: Reader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize {
72 const vtable_seekRead = r.vtable.seekRead.?;74 const vtable_posRead = r.vtable.posRead.?;
73 var offset: u64 = start_offset;75 var offset: u64 = start_offset;
74 while (true) {76 while (true) {
75 const status = try vtable_seekRead(r.context, w, .none, offset);77 const status = try vtable_posRead(r.context, w, .none, offset);
76 offset += status.len;78 offset += status.len;
77 if (status.end) return @intCast(offset - start_offset);79 if (status.end) return @intCast(offset - start_offset);
78 }80 }
...@@ -130,6 +132,10 @@ pub fn buffered(r: Reader, buffer: []u8) std.io.BufferedReader {...@@ -130,6 +132,10 @@ pub fn buffered(r: Reader, buffer: []u8) std.io.BufferedReader {
130 };132 };
131}133}
132134
135pub fn unbuffered(r: Reader) std.io.BufferedReader {
136 return buffered(r, &.{});
137}
138
133pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader {139pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader {
134 return .{140 return .{
135 .reader = r,141 .reader = r,
...@@ -140,10 +146,6 @@ pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader {...@@ -140,10 +146,6 @@ pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader {
140 };146 };
141}147}
142148
143pub fn unbuffered(r: Reader) std.io.BufferedReader {
144 return buffered(r, &.{});
145}
146
147test "when the backing reader provides one byte at a time" {149test "when the backing reader provides one byte at a time" {
148 const OneByteReader = struct {150 const OneByteReader = struct {
149 str: []const u8,151 str: []const u8,
lib/std/io/Writer.zig+3-3
...@@ -80,7 +80,7 @@ pub fn writeFile(...@@ -80,7 +80,7 @@ pub fn writeFile(
80 w: Writer,80 w: Writer,
81 file: std.fs.File,81 file: std.fs.File,
82 offset: u64,82 offset: u64,
83 len: VTable.FileLen,83 len: FileLen,
84 headers_and_trailers: []const []const u8,84 headers_and_trailers: []const []const u8,
85 headers_len: usize,85 headers_len: usize,
86) anyerror!usize {86) anyerror!usize {
...@@ -91,7 +91,7 @@ pub fn unimplemented_writeFile(...@@ -91,7 +91,7 @@ pub fn unimplemented_writeFile(
91 context: *anyopaque,91 context: *anyopaque,
92 file: std.fs.File,92 file: std.fs.File,
93 offset: u64,93 offset: u64,
94 len: VTable.FileLen,94 len: FileLen,
95 headers_and_trailers: []const []const u8,95 headers_and_trailers: []const []const u8,
96 headers_len: usize,96 headers_len: usize,
97) anyerror!usize {97) anyerror!usize {
...@@ -107,7 +107,7 @@ pub fn unimplemented_writeFile(...@@ -107,7 +107,7 @@ pub fn unimplemented_writeFile(
107pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {107pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
108 return .{108 return .{
109 .buffer = .initBuffer(buffer),109 .buffer = .initBuffer(buffer),
110 .mode = .{ .writer = w },110 .unbuffered_writer = w,
111 };111 };
112}112}
113113
lib/std/net.zig+1-1
...@@ -2011,7 +2011,7 @@ pub const Stream = struct {...@@ -2011,7 +2011,7 @@ pub const Stream = struct {
2011 context: *anyopaque,2011 context: *anyopaque,
2012 in_file: std.fs.File,2012 in_file: std.fs.File,
2013 in_offset: u64,2013 in_offset: u64,
2014 in_len: std.io.Writer.VTable.FileLen,2014 in_len: std.io.Writer.FileLen,
2015 headers_and_trailers: []const []const u8,2015 headers_and_trailers: []const []const u8,
2016 headers_len: usize,2016 headers_len: usize,
2017 ) anyerror!usize {2017 ) anyerror!usize {