| author | |
| committer | |
| log | 57e1fc29d2728559928ae6f99ebe248c07f5ce18 |
| tree | 6d7cae37db09693e5972b79f82ddf7fbec77acdf |
| parent | 5aa8573f2bc8fad297a63ce798ed5ede5b5833ac |
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 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | |
| 5 | pub fn bitReader(comptime T: type, reader: anytype) BitReader(T, @TypeOf(reader)) { | |
| 6 | return BitReader(T, @TypeOf(reader)).init(reader); | |
| 7 | } | |
| 8 | ||
| 9 | pub fn BitReader64(comptime ReaderType: type) type { | |
| 10 | return BitReader(u64, ReaderType); | |
| 11 | } | |
| 12 | ||
| 13 | pub fn BitReader32(comptime ReaderType: type) type { | |
| 14 | return BitReader(u32, ReaderType); | |
| 15 | } | |
| 5 | pub 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 | }; | |
| 16 | 13 | |
| 17 | 14 | /// Bit reader used during inflate (decompression). Has internal buffer of 64 |
| 18 | 15 | /// 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 | 20 | /// fill buffer from forward_reader by calling fill in advance and readF with |
| 24 | 21 | /// buffered flag set. |
| 25 | 22 | /// |
| 26 | pub fn BitReader(comptime T: type, comptime ReaderType: type) type { | |
| 23 | pub fn BitReader(comptime T: type) type { | |
| 27 | 24 | assert(T == u32 or T == u64); |
| 28 | 25 | const t_bytes: usize = @sizeOf(T); |
| 29 | 26 | const Tshift = if (T == u64) u6 else u5; |
| 30 | 27 | |
| 31 | 28 | return struct { |
| 32 | 29 | // Underlying reader used for filling internal bits buffer |
| 33 | forward_reader: ReaderType = undefined, | |
| 30 | forward_reader: *std.io.BufferedReader, | |
| 34 | 31 | // Internal buffer of 64 bits |
| 35 | 32 | bits: T = 0, |
| 36 | 33 | // Number of bits in the buffer |
| ... | ... | @@ -38,10 +35,8 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type { |
| 38 | 35 | |
| 39 | 36 | const Self = @This(); |
| 40 | 37 | |
| 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 }; | |
| 45 | 40 | self.fill(1) catch {}; |
| 46 | 41 | return self; |
| 47 | 42 | } |
| ... | ... | @@ -55,7 +50,7 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type { |
| 55 | 50 | /// bits to decode. So `nice` is not hard limit, it will just try to have |
| 56 | 51 | /// that number of bits available. If end of forward stream is reached |
| 57 | 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 | 54 | if (self.nbits >= nice and nice != 0) { |
| 60 | 55 | return; // We have enough bits |
| 61 | 56 | } |
| ... | ... | @@ -86,31 +81,29 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type { |
| 86 | 81 | // First read from internal bits buffer. |
| 87 | 82 | var n: usize = 0; |
| 88 | 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 | 85 | n += 1; |
| 91 | 86 | } |
| 92 | 87 | // Then use forward reader for all other bytes. |
| 93 | 88 | try self.forward_reader.readNoEof(buf[n..]); |
| 94 | 89 | } |
| 95 | 90 | |
| 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 | 91 | /// Alias for readF(U, 0). |
| 103 | 92 | pub fn read(self: *Self, comptime U: type) !U { |
| 104 | 93 | return self.readF(U, 0); |
| 105 | 94 | } |
| 106 | 95 | |
| 107 | 96 | /// 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 | }); | |
| 110 | 103 | } |
| 111 | 104 | |
| 112 | 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 | 107 | if (U == T) { |
| 115 | 108 | assert(how == 0); |
| 116 | 109 | assert(self.alignBits() == 0); |
| ... | ... | @@ -129,34 +122,35 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type { |
| 129 | 122 | try self.shift(n); // advance buffer for n |
| 130 | 123 | return u; |
| 131 | 124 | }, |
| 132 | (flag.peek) => { // no shift, leave bits in the buffer | |
| 125 | .{ .peek = true } => { // no shift, leave bits in the buffer | |
| 133 | 126 | try self.fill(n); |
| 134 | 127 | return @truncate(self.bits); |
| 135 | 128 | }, |
| 136 | flag.buffered => { // no fill, assume that buffer has enough bits | |
| 129 | .{ .buffered = true } => { // no fill, assume that buffer has enough bits | |
| 137 | 130 | const u: U = @truncate(self.bits); |
| 138 | 131 | try self.shift(n); |
| 139 | 132 | return u; |
| 140 | 133 | }, |
| 141 | (flag.reverse) => { // same as 0 with bit reverse | |
| 134 | .{ .reverse = true } => { // same as 0 with bit reverse | |
| 142 | 135 | try self.fill(n); |
| 143 | 136 | const u: U = @truncate(self.bits); |
| 144 | 137 | try self.shift(n); |
| 145 | 138 | return @bitReverse(u); |
| 146 | 139 | }, |
| 147 | (flag.peek | flag.reverse) => { | |
| 140 | .{ .peek = true, .reverse = true } => { | |
| 148 | 141 | try self.fill(n); |
| 149 | 142 | return @bitReverse(@as(U, @truncate(self.bits))); |
| 150 | 143 | }, |
| 151 | (flag.buffered | flag.reverse) => { | |
| 144 | .{ .buffered = true, .reverse = true } => { | |
| 152 | 145 | const u: U = @truncate(self.bits); |
| 153 | 146 | try self.shift(n); |
| 154 | 147 | return @bitReverse(u); |
| 155 | 148 | }, |
| 156 | (flag.peek | flag.buffered) => { | |
| 149 | .{ .peek = true, .buffered = true }, | |
| 150 | => { | |
| 157 | 151 | return @truncate(self.bits); |
| 158 | 152 | }, |
| 159 | (flag.peek | flag.buffered | flag.reverse) => { | |
| 153 | .{ .peek = true, .buffered = true, .reverse = true } => { | |
| 160 | 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 | 163 | 0 => { |
| 170 | 164 | try self.fill(n); |
| 171 | 165 | }, |
| 172 | flag.buffered => {}, | |
| 166 | .{ .buffered = true } => {}, | |
| 173 | 167 | else => unreachable, |
| 174 | 168 | } |
| 175 | 169 | const mask: u16 = (@as(u16, 1) << n) - 1; |
| ... | ... | @@ -226,24 +220,24 @@ pub fn BitReader(comptime T: type, comptime ReaderType: type) type { |
| 226 | 220 | /// 11000111 |
| 227 | 221 | pub fn readFixedCode(self: *Self) !u16 { |
| 228 | 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 | 224 | if (code7 <= 0b0010_111) { // 7 bits, 256-279, codes 0000_000 - 0010_111 |
| 231 | 225 | return @as(u16, code7) + 256; |
| 232 | 226 | } 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 | 228 | } 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 | 230 | } 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 | } |
| 242 | 236 | |
| 243 | 237 | test "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); | |
| 247 | 241 | |
| 248 | 242 | try testing.expectEqual(@as(u8, 48), br.nbits); |
| 249 | 243 | try testing.expectEqual(@as(u64, 0xc9cd48f3), br.bits); |
| ... | ... | @@ -253,8 +247,8 @@ test "readF" { |
| 253 | 247 | try testing.expectEqual(@as(u8, 48 - 3), br.nbits); |
| 254 | 248 | try testing.expectEqual(@as(u3, 5), br.alignBits()); |
| 255 | 249 | |
| 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); | |
| 258 | 252 | try br.shift(9); |
| 259 | 253 | try testing.expectEqual(@as(u8, 36), br.nbits); |
| 260 | 254 | try testing.expectEqual(@as(u3, 4), br.alignBits()); |
| ... | ... | @@ -283,15 +277,15 @@ test "read block type 1 data" { |
| 283 | 277 | 0x0c, 0x01, 0x02, 0x03, // |
| 284 | 278 | 0xaa, 0xbb, 0xcc, 0xdd, |
| 285 | 279 | }; |
| 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); | |
| 289 | 283 | |
| 290 | 284 | try testing.expectEqual(@as(u1, 1), try br.readF(u1, 0)); // bfinal |
| 291 | 285 | try testing.expectEqual(@as(u2, 1), try br.readF(u2, 0)); // block_type |
| 292 | 286 | |
| 293 | 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 | 290 | try testing.expectEqual(@as(u7, 0), try br.readF(u7, 0)); // end of block |
| 297 | 291 | br.alignToByte(); |
| ... | ... | @@ -306,8 +300,9 @@ test "shift/fill" { |
| 306 | 300 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 307 | 301 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 308 | 302 | }; |
| 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); | |
| 311 | 306 | |
| 312 | 307 | try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits); |
| 313 | 308 | try br.shift(8); |
| ... | ... | @@ -332,8 +327,9 @@ test "readAll" { |
| 332 | 327 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 333 | 328 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 334 | 329 | }; |
| 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); | |
| 337 | 333 | |
| 338 | 334 | switch (T) { |
| 339 | 335 | u64 => try testing.expectEqual(@as(u64, 0x08_07_06_05_04_03_02_01), br.bits), |
| ... | ... | @@ -354,8 +350,9 @@ test "readFixedCode" { |
| 354 | 350 | inline for ([_]type{ u64, u32 }) |T| { |
| 355 | 351 | const fixed_codes = @import("huffman_encoder.zig").fixed_codes; |
| 356 | 352 | |
| 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); | |
| 359 | 356 | |
| 360 | 357 | for (0..286) |c| { |
| 361 | 358 | try testing.expectEqual(c, try rdr.readFixedCode()); |
| ... | ... | @@ -369,8 +366,9 @@ test "u32 leaves no bits on u32 reads" { |
| 369 | 366 | 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
| 370 | 367 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
| 371 | 368 | }; |
| 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); | |
| 374 | 372 | |
| 375 | 373 | _ = try br.read(u3); |
| 376 | 374 | try testing.expectEqual(29, br.nbits); |
| ... | ... | @@ -396,8 +394,9 @@ test "u64 need fill after alignToByte" { |
| 396 | 394 | }; |
| 397 | 395 | |
| 398 | 396 | // 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); | |
| 401 | 400 | _ = try br.read(u23); |
| 402 | 401 | try testing.expectEqual(41, br.nbits); |
| 403 | 402 | br.alignToByte(); |
| ... | ... | @@ -409,7 +408,7 @@ test "u64 need fill after alignToByte" { |
| 409 | 408 | |
| 410 | 409 | // fill after align ensures all bits filled |
| 411 | 410 | fbs.reset(); |
| 412 | br = bitReader(u64, fbs.reader()); | |
| 411 | br = .init(&fbs); | |
| 413 | 412 | _ = try br.read(u23); |
| 414 | 413 | try testing.expectEqual(41, br.nbits); |
| 415 | 414 | br.alignToByte(); |
lib/std/compress/lzma2.zig+4-8| ... | ... | @@ -3,11 +3,7 @@ const Allocator = std.mem.Allocator; |
| 3 | 3 | |
| 4 | 4 | pub const decode = @import("lzma2/decode.zig"); |
| 5 | 5 | |
| 6 | pub fn decompress( | |
| 7 | allocator: Allocator, | |
| 8 | reader: anytype, | |
| 9 | writer: anytype, | |
| 10 | ) !void { | |
| 6 | pub fn decompress(allocator: Allocator, reader: *std.io.BufferedReader, writer: *std.io.BufferedWriter) !void { | |
| 11 | 7 | var decoder = try decode.Decoder.init(allocator); |
| 12 | 8 | defer decoder.deinit(allocator); |
| 13 | 9 | return decoder.decompress(allocator, reader, writer); |
| ... | ... | @@ -19,11 +15,11 @@ test { |
| 19 | 15 | 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x02, |
| 20 | 16 | 0x00, 0x06, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x0A, 0x00, |
| 21 | 17 | }; |
| 22 | var stream: std.io.FixedBufferStream = .{ .buffer = compressed }; | |
| 23 | ||
| 18 | var stream: std.io.BufferedReader = undefined; | |
| 19 | stream.initFixed(&compressed); | |
| 24 | 20 | var decomp: std.io.AllocatingWriter = undefined; |
| 25 | 21 | const decomp_bw = decomp.init(std.testing.allocator); |
| 26 | 22 | defer decomp.deinit(); |
| 27 | try decompress(std.testing.allocator, stream.reader(), decomp_bw); | |
| 23 | try decompress(std.testing.allocator, &stream, decomp_bw); | |
| 28 | 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 | 2235 | |
| 2236 | 2236 | const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size); |
| 2237 | 2237 | 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; | |
| 2241 | 2241 | if (chdr.ch_type != .ZLIB) continue; |
| 2242 | const ch_size = chdr.ch_size; | |
| 2242 | 2243 | |
| 2243 | var zlib_stream = std.compress.zlib.decompressor(section_reader); | |
| 2244 | var zlib_stream = std.compress.zlib.decompressor(&section_reader); | |
| 2244 | 2245 | |
| 2245 | const decompressed_section = try gpa.alloc(u8, chdr.ch_size); | |
| 2246 | const decompressed_section = try gpa.alloc(u8, ch_size); | |
| 2246 | 2247 | errdefer gpa.free(decompressed_section); |
| 2247 | 2248 | |
| 2248 | 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 | 1590 | return .{ |
| 1591 | 1591 | .context = handleToOpaque(file.handle), |
| 1592 | 1592 | .vtable = .{ |
| 1593 | .seekRead = reader_seekRead, | |
| 1593 | .posRead = reader_posRead, | |
| 1594 | .posReadVec = reader_posReadVec, | |
| 1594 | 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 | 1612 | /// vectors through the underlying write calls as possible. |
| 1611 | 1613 | const max_buffers_len = 16; |
| 1612 | 1614 | |
| 1613 | pub fn reader_seekRead( | |
| 1615 | pub fn reader_posRead( | |
| 1614 | 1616 | context: *anyopaque, |
| 1615 | 1617 | bw: *std.io.BufferedWriter, |
| 1616 | 1618 | limit: std.io.Reader.Limit, |
| ... | ... | @@ -1621,10 +1623,36 @@ pub fn reader_seekRead( |
| 1621 | 1623 | return writer.writeFile(bw, file, .init(offset), len, &.{}, 0); |
| 1622 | 1624 | } |
| 1623 | 1625 | |
| 1624 | pub fn reader_streamRead(context: *anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Reader.Limit) anyerror!usize { | |
| 1626 | pub 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 | ||
| 1635 | pub fn reader_streamRead( | |
| 1636 | context: *anyopaque, | |
| 1637 | bw: *std.io.BufferedWriter, | |
| 1638 | limit: std.io.Reader.Limit, | |
| 1639 | ) anyerror!std.io.Reader.Status { | |
| 1625 | 1640 | const file = opaqueToHandle(context); |
| 1626 | 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 | ||
| 1649 | pub 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 | } |
| 1629 | 1657 | |
| 1630 | 1658 | pub fn writer_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { |
| ... | ... | @@ -1671,7 +1699,7 @@ pub fn writer_writeFile( |
| 1671 | 1699 | context: *anyopaque, |
| 1672 | 1700 | in_file: std.fs.File, |
| 1673 | 1701 | in_offset: u64, |
| 1674 | in_len: std.io.Writer.VTable.FileLen, | |
| 1702 | in_len: std.io.Writer.FileLen, | |
| 1675 | 1703 | headers_and_trailers: []const []const u8, |
| 1676 | 1704 | headers_len: usize, |
| 1677 | 1705 | ) anyerror!usize { |
| ... | ... | @@ -1705,7 +1733,7 @@ fn writeFileUnseekable( |
| 1705 | 1733 | out_fd: Handle, |
| 1706 | 1734 | in_fd: Handle, |
| 1707 | 1735 | in_offset: u64, |
| 1708 | in_len: std.io.Writer.VTable.FileLen, | |
| 1736 | in_len: std.io.Writer.FileLen, | |
| 1709 | 1737 | headers_and_trailers: []const []const u8, |
| 1710 | 1738 | headers_len: usize, |
| 1711 | 1739 | ) anyerror!usize { |
lib/std/http/Client.zig+2-2| ... | ... | @@ -1119,7 +1119,7 @@ pub const Request = struct { |
| 1119 | 1119 | context: *anyopaque, |
| 1120 | 1120 | file: std.fs.File, |
| 1121 | 1121 | offset: u64, |
| 1122 | len: std.io.Writer.VTable.FileLen, | |
| 1122 | len: std.io.Writer.FileLen, | |
| 1123 | 1123 | headers_and_trailers: []const []const u8, |
| 1124 | 1124 | headers_len: usize, |
| 1125 | 1125 | ) anyerror!usize { |
| ... | ... | @@ -1159,7 +1159,7 @@ pub const Request = struct { |
| 1159 | 1159 | context: *anyopaque, |
| 1160 | 1160 | file: std.fs.File, |
| 1161 | 1161 | offset: u64, |
| 1162 | len: std.io.Writer.VTable.FileLen, | |
| 1162 | len: std.io.Writer.FileLen, | |
| 1163 | 1163 | headers_and_trailers: []const []const u8, |
| 1164 | 1164 | headers_len: usize, |
| 1165 | 1165 | ) anyerror!usize { |
lib/std/http/Server.zig+2-2| ... | ... | @@ -881,7 +881,7 @@ pub const Response = struct { |
| 881 | 881 | context: *anyopaque, |
| 882 | 882 | file: std.fs.File, |
| 883 | 883 | offset: u64, |
| 884 | len: std.io.Writer.VTable.FileLen, | |
| 884 | len: std.io.Writer.FileLen, | |
| 885 | 885 | headers_and_trailers: []const []const u8, |
| 886 | 886 | headers_len: usize, |
| 887 | 887 | ) anyerror!usize { |
| ... | ... | @@ -948,7 +948,7 @@ pub const Response = struct { |
| 948 | 948 | context: *anyopaque, |
| 949 | 949 | file: std.fs.File, |
| 950 | 950 | offset: u64, |
| 951 | len: std.io.Writer.VTable.FileLen, | |
| 951 | len: std.io.Writer.FileLen, | |
| 952 | 952 | headers_and_trailers: []const []const u8, |
| 953 | 953 | headers_len: usize, |
| 954 | 954 | ) anyerror!usize { |
lib/std/io.zig+1-1| ... | ... | @@ -118,7 +118,7 @@ fn null_writeFile( |
| 118 | 118 | context: *anyopaque, |
| 119 | 119 | file: std.fs.File, |
| 120 | 120 | offset: u64, |
| 121 | len: Writer.VTable.FileLen, | |
| 121 | len: Writer.FileLen, | |
| 122 | 122 | headers_and_trailers: []const []const u8, |
| 123 | 123 | headers_len: usize, |
| 124 | 124 | ) anyerror!usize { |
lib/std/io/AllocatingWriter.zig+1-1| ... | ... | @@ -153,7 +153,7 @@ fn writeFile( |
| 153 | 153 | context: *anyopaque, |
| 154 | 154 | file: std.fs.File, |
| 155 | 155 | offset: u64, |
| 156 | len: std.io.Writer.VTable.FileLen, | |
| 156 | len: std.io.Writer.FileLen, | |
| 157 | 157 | headers_and_trailers_full: []const []const u8, |
| 158 | 158 | headers_len_full: usize, |
| 159 | 159 | ) anyerror!usize { |
lib/std/io/BufferedReader.zig+21-4| ... | ... | @@ -25,7 +25,7 @@ pub fn initFixed(br: *BufferedReader, buffer: []const u8) void { |
| 25 | 25 | .context = br, |
| 26 | 26 | .vtable = &.{ |
| 27 | 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 | 44 | .context = br, |
| 45 | 45 | .vtable = &.{ |
| 46 | 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 | 67 | return br.unbuffered_reader.streamRead(bw, limit); |
| 66 | 68 | } |
| 67 | 69 | |
| 68 | fn passthru_seekRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit, off: u64) anyerror!Reader.Status { | |
| 70 | fn 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 | ||
| 77 | fn passthru_posRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit, off: u64) anyerror!Reader.Status { | |
| 69 | 78 | const br: *BufferedReader = @alignCast(@ptrCast(ctx)); |
| 70 | 79 | const buffer = br.storage.buffer.items; |
| 71 | 80 | if (off < buffer.len) { |
| 72 | 81 | const send = buffer[off..limit.min(buffer.len)]; |
| 73 | 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 | ||
| 87 | fn 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 | } |
| 77 | 94 | |
| 78 | 95 | /// 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 | 10 | /// |
| 11 | 11 | /// If this has capacity zero, the writer is unbuffered, and `flush` is a no-op. |
| 12 | 12 | buffer: std.ArrayListUnmanaged(u8), |
| 13 | mode: 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 | ||
| 30 | pub 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. | |
| 21 | unbuffered_writer: Writer, | |
| 37 | 22 | |
| 38 | 23 | /// Number of slices to store on the stack, when trying to send as many byte |
| 39 | 24 | /// vectors through the underlying write calls as possible. |
| ... | ... | @@ -72,23 +57,24 @@ pub fn initFixed(bw: *BufferedWriter, buffer: []u8) void { |
| 72 | 57 | /// This function is available when using `initFixed`. |
| 73 | 58 | pub fn getWritten(bw: *const BufferedWriter) []u8 { |
| 74 | 59 | assert(bw.unbuffered_writer.vtable == &fixed_vtable); |
| 75 | return bw.buffer[0..bw.end]; | |
| 60 | return bw.buffer.items; | |
| 76 | 61 | } |
| 77 | 62 | |
| 78 | 63 | /// This function is available when using `initFixed`. |
| 79 | 64 | pub fn reset(bw: *BufferedWriter) void { |
| 80 | 65 | assert(bw.unbuffered_writer.vtable == &fixed_vtable); |
| 81 | bw.end = 0; | |
| 66 | bw.buffer.items.len = 0; | |
| 82 | 67 | } |
| 83 | 68 | |
| 84 | 69 | pub 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 | 72 | try bw.unbuffered_writer.writeAll(send_buffer); |
| 87 | bw.end = 0; | |
| 73 | list.items.len = 0; | |
| 88 | 74 | } |
| 89 | 75 | |
| 90 | 76 | pub fn unusedCapacitySlice(bw: *const BufferedWriter) []u8 { |
| 91 | return bw.buffer[bw.end..]; | |
| 77 | return bw.buffer.unusedCapacitySlice(); | |
| 92 | 78 | } |
| 93 | 79 | |
| 94 | 80 | /// 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 | 102 | |
| 117 | 103 | fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { |
| 118 | 104 | 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; | |
| 121 | 108 | |
| 122 | 109 | var buffers: [max_buffers_len][]const u8 = undefined; |
| 123 | var end = bw.end; | |
| 110 | var end = start_end; | |
| 124 | 111 | for (data, 0..) |bytes, i| { |
| 125 | 112 | const new_end = end + bytes.len; |
| 126 | 113 | if (new_end <= buffer.len) { |
| ... | ... | @@ -144,10 +131,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi |
| 144 | 131 | @branchHint(.unlikely); |
| 145 | 132 | const remainder = buffer[n..end]; |
| 146 | 133 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 147 | bw.end = remainder.len; | |
| 134 | list.items.len = remainder.len; | |
| 148 | 135 | return end - start_end; |
| 149 | 136 | } |
| 150 | bw.end = 0; | |
| 137 | list.items.len = 0; | |
| 151 | 138 | return n - start_end; |
| 152 | 139 | } |
| 153 | 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 | 142 | @branchHint(.unlikely); |
| 156 | 143 | const remainder = buffer[n..end]; |
| 157 | 144 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 158 | bw.end = remainder.len; | |
| 145 | list.items.len = remainder.len; | |
| 159 | 146 | return end - start_end; |
| 160 | 147 | } |
| 161 | bw.end = 0; | |
| 148 | list.items.len = 0; | |
| 162 | 149 | return n - start_end; |
| 163 | 150 | } |
| 164 | 151 | |
| ... | ... | @@ -168,7 +155,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi |
| 168 | 155 | @branchHint(.unlikely); |
| 169 | 156 | // It was added in the loop above; undo it here. |
| 170 | 157 | end -= pattern.len; |
| 171 | bw.end = end; | |
| 158 | list.items.len = end; | |
| 172 | 159 | return end - start_end; |
| 173 | 160 | } |
| 174 | 161 | |
| ... | ... | @@ -176,7 +163,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi |
| 176 | 163 | |
| 177 | 164 | switch (pattern.len) { |
| 178 | 165 | 0 => { |
| 179 | bw.end = end; | |
| 166 | list.items.len = end; | |
| 180 | 167 | return end - start_end; |
| 181 | 168 | }, |
| 182 | 169 | 1 => { |
| ... | ... | @@ -184,7 +171,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi |
| 184 | 171 | if (new_end <= buffer.len) { |
| 185 | 172 | @branchHint(.likely); |
| 186 | 173 | @memset(buffer[end..new_end], pattern[0]); |
| 187 | bw.end = new_end; | |
| 174 | list.items.len = new_end; | |
| 188 | 175 | return new_end - start_end; |
| 189 | 176 | } |
| 190 | 177 | buffers[0] = buffer[0..end]; |
| ... | ... | @@ -194,10 +181,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi |
| 194 | 181 | @branchHint(.unlikely); |
| 195 | 182 | const remainder = buffer[n..end]; |
| 196 | 183 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 197 | bw.end = remainder.len; | |
| 184 | list.items.len = remainder.len; | |
| 198 | 185 | return end - start_end; |
| 199 | 186 | } |
| 200 | bw.end = 0; | |
| 187 | list.items.len = 0; | |
| 201 | 188 | return n - start_end; |
| 202 | 189 | }, |
| 203 | 190 | else => { |
| ... | ... | @@ -207,7 +194,7 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi |
| 207 | 194 | while (end < new_end) : (end += pattern.len) { |
| 208 | 195 | @memcpy(buffer[end..][0..pattern.len], pattern); |
| 209 | 196 | } |
| 210 | bw.end = new_end; | |
| 197 | list.items.len = new_end; | |
| 211 | 198 | return new_end - start_end; |
| 212 | 199 | } |
| 213 | 200 | buffers[0] = buffer[0..end]; |
| ... | ... | @@ -217,10 +204,10 @@ fn passthru_writeSplat(context: *anyopaque, data: []const []const u8, splat: usi |
| 217 | 204 | @branchHint(.unlikely); |
| 218 | 205 | const remainder = buffer[n..end]; |
| 219 | 206 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 220 | bw.end = remainder.len; | |
| 207 | list.items.len = remainder.len; | |
| 221 | 208 | return end - start_end; |
| 222 | 209 | } |
| 223 | bw.end = 0; | |
| 210 | list.items.len = 0; | |
| 224 | 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 | 215 | |
| 229 | 216 | fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize { |
| 230 | 217 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 218 | const list = &bw.buffer; | |
| 231 | 219 | // When this function is called it means the buffer got full, so it's time |
| 232 | 220 | // to return an error. However, we still need to make sure all of the |
| 233 | 221 | // available buffer has been used. |
| 234 | 222 | const first = data[0]; |
| 235 | const dest = bw.buffer[bw.end..]; | |
| 223 | const dest = list.unusedCapacitySlice(); | |
| 236 | 224 | @memcpy(dest, first[0..dest.len]); |
| 225 | list.items.len = list.capacity; | |
| 237 | 226 | return error.NoSpaceLeft; |
| 238 | 227 | } |
| 239 | 228 | |
| ... | ... | @@ -242,26 +231,29 @@ fn fixed_writev(context: *anyopaque, data: []const []const u8) anyerror!usize { |
| 242 | 231 | /// available buffer has been filled. |
| 243 | 232 | fn fixed_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize { |
| 244 | 233 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 234 | const list = &bw.buffer; | |
| 245 | 235 | for (data) |bytes| { |
| 246 | const dest = bw.buffer[bw.end..]; | |
| 236 | const dest = list.unusedCapacitySlice(); | |
| 247 | 237 | if (dest.len == 0) return error.NoSpaceLeft; |
| 248 | 238 | const len = @min(bytes.len, dest.len); |
| 249 | 239 | @memcpy(dest[0..len], bytes[0..len]); |
| 250 | bw.end += len; | |
| 240 | list.items.len += len; | |
| 251 | 241 | } |
| 252 | 242 | const pattern = data[data.len - 1]; |
| 253 | const dest = bw.buffer[bw.end..]; | |
| 243 | const dest = list.unusedCapacitySlice(); | |
| 254 | 244 | switch (pattern.len) { |
| 255 | 245 | 0 => unreachable, |
| 256 | 246 | 1 => @memset(dest, pattern[0]), |
| 257 | 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 | 250 | return error.NoSpaceLeft; |
| 260 | 251 | } |
| 261 | 252 | |
| 262 | 253 | pub 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; | |
| 265 | 257 | const new_end = end + bytes.len; |
| 266 | 258 | if (new_end > buffer.len) { |
| 267 | 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 | 262 | @branchHint(.unlikely); |
| 271 | 263 | const remainder = buffer[n..end]; |
| 272 | 264 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 273 | bw.end = remainder.len; | |
| 265 | list.items.len = remainder.len; | |
| 274 | 266 | return 0; |
| 275 | 267 | } |
| 276 | bw.end = 0; | |
| 268 | list.items.len = 0; | |
| 277 | 269 | return n - end; |
| 278 | 270 | } |
| 279 | 271 | @memcpy(buffer[end..new_end], bytes); |
| 280 | bw.end = new_end; | |
| 272 | list.items.len = new_end; | |
| 281 | 273 | return bytes.len; |
| 282 | 274 | } |
| 283 | 275 | |
| ... | ... | @@ -302,35 +294,29 @@ pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void { |
| 302 | 294 | list.items.len = buffer.len + 1; |
| 303 | 295 | return; |
| 304 | 296 | } |
| 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; | |
| 330 | 312 | return; |
| 331 | 313 | } |
| 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; | |
| 334 | 320 | } |
| 335 | 321 | } |
| 336 | 322 | |
| ... | ... | @@ -395,7 +381,7 @@ pub fn writeFile( |
| 395 | 381 | bw: *BufferedWriter, |
| 396 | 382 | file: std.fs.File, |
| 397 | 383 | offset: u64, |
| 398 | len: Writer.VTable.FileLen, | |
| 384 | len: Writer.FileLen, | |
| 399 | 385 | headers_and_trailers: []const []const u8, |
| 400 | 386 | headers_len: usize, |
| 401 | 387 | ) anyerror!usize { |
| ... | ... | @@ -406,14 +392,15 @@ fn passthru_writeFile( |
| 406 | 392 | context: *anyopaque, |
| 407 | 393 | file: std.fs.File, |
| 408 | 394 | offset: u64, |
| 409 | len: Writer.VTable.FileLen, | |
| 395 | len: Writer.FileLen, | |
| 410 | 396 | headers_and_trailers: []const []const u8, |
| 411 | 397 | headers_len: usize, |
| 412 | 398 | ) anyerror!usize { |
| 413 | 399 | const bw: *BufferedWriter = @alignCast(@ptrCast(context)); |
| 414 | const buffer = bw.buffer; | |
| 400 | const list = &bw.buffer; | |
| 401 | const buffer = list.allocatedSlice(); | |
| 415 | 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 | 404 | const headers = headers_and_trailers[0..headers_len]; |
| 418 | 405 | const trailers = headers_and_trailers[headers_len..]; |
| 419 | 406 | var buffers: [max_buffers_len][]const u8 = undefined; |
| ... | ... | @@ -443,10 +430,10 @@ fn passthru_writeFile( |
| 443 | 430 | @branchHint(.unlikely); |
| 444 | 431 | const remainder = buffer[n..end]; |
| 445 | 432 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 446 | bw.end = remainder.len; | |
| 433 | list.items.len = remainder.len; | |
| 447 | 434 | return end - start_end; |
| 448 | 435 | } |
| 449 | bw.end = 0; | |
| 436 | list.items.len = 0; | |
| 450 | 437 | return n - start_end; |
| 451 | 438 | } |
| 452 | 439 | // Have not made it past the headers yet; must call `writev`. |
| ... | ... | @@ -455,10 +442,10 @@ fn passthru_writeFile( |
| 455 | 442 | @branchHint(.unlikely); |
| 456 | 443 | const remainder = buffer[n..end]; |
| 457 | 444 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 458 | bw.end = remainder.len; | |
| 445 | list.items.len = remainder.len; | |
| 459 | 446 | return end - start_end; |
| 460 | 447 | } |
| 461 | bw.end = 0; | |
| 448 | list.items.len = 0; | |
| 462 | 449 | return n - start_end; |
| 463 | 450 | } |
| 464 | 451 | // All headers written to buffer. |
| ... | ... | @@ -473,10 +460,10 @@ fn passthru_writeFile( |
| 473 | 460 | @branchHint(.unlikely); |
| 474 | 461 | const remainder = buffer[n..end]; |
| 475 | 462 | std.mem.copyForwards(u8, buffer[0..remainder.len], remainder); |
| 476 | bw.end = remainder.len; | |
| 463 | list.items.len = remainder.len; | |
| 477 | 464 | return end - start_end; |
| 478 | 465 | } |
| 479 | bw.end = 0; | |
| 466 | list.items.len = 0; | |
| 480 | 467 | return n - start_end; |
| 481 | 468 | } |
| 482 | 469 | |
| ... | ... | @@ -484,7 +471,7 @@ pub const WriteFileOptions = struct { |
| 484 | 471 | offset: u64 = 0, |
| 485 | 472 | /// If the size of the source file is known, it is likely that passing the |
| 486 | 473 | /// size here will save one syscall. |
| 487 | len: Writer.VTable.FileLen = .entire_file, | |
| 474 | len: Writer.FileLen = .entire_file, | |
| 488 | 475 | /// Headers and trailers must be passed together so that in case `len` is |
| 489 | 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 | 33 | context: *anyopaque, |
| 34 | 34 | file: std.fs.File, |
| 35 | 35 | offset: u64, |
| 36 | len: Writer.VTable.FileLen, | |
| 36 | len: Writer.FileLen, | |
| 37 | 37 | headers_and_trailers: []const []const u8, |
| 38 | 38 | headers_len: usize, |
| 39 | 39 | ) anyerror!usize { |
lib/std/io/Reader.zig+13-11| ... | ... | @@ -19,10 +19,11 @@ pub const VTable = struct { |
| 19 | 19 | /// |
| 20 | 20 | /// If this is `null` it is equivalent to always returning |
| 21 | 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, | |
| 23 | 24 | |
| 24 | 25 | /// 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 | |
| 26 | 27 | /// instead. |
| 27 | 28 | /// |
| 28 | 29 | /// Returns the number of bytes written, which will be at minimum `0` and at |
| ... | ... | @@ -37,9 +38,10 @@ pub const VTable = struct { |
| 37 | 38 | /// If this is `null` it is equivalent to always returning |
| 38 | 39 | /// `error.Unstreamable`. |
| 39 | 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 | }; |
| 41 | 43 | |
| 42 | pub const Len = @Type(.{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 }); | |
| 44 | pub const Len = @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } }); | |
| 43 | 45 | |
| 44 | 46 | pub const Status = packed struct(usize) { |
| 45 | 47 | /// Number of bytes that were written to `writer`. |
| ... | ... | @@ -56,7 +58,7 @@ pub const Limit = enum(usize) { |
| 56 | 58 | /// Returns total number of bytes written to `w`. |
| 57 | 59 | pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize { |
| 58 | 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 | 62 | error.Unseekable => {}, |
| 61 | 63 | else => return err, |
| 62 | 64 | }; |
| ... | ... | @@ -68,11 +70,11 @@ pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize { |
| 68 | 70 | /// |
| 69 | 71 | /// May return `error.Unseekable`, indicating this function cannot be used to |
| 70 | 72 | /// read from the reader. |
| 71 | pub fn seekReadAll(r: Reader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize { | |
| 72 | const vtable_seekRead = r.vtable.seekRead.?; | |
| 73 | pub fn posReadAll(r: Reader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize { | |
| 74 | const vtable_posRead = r.vtable.posRead.?; | |
| 73 | 75 | var offset: u64 = start_offset; |
| 74 | 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 | 78 | offset += status.len; |
| 77 | 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 | 132 | }; |
| 131 | 133 | } |
| 132 | 134 | |
| 135 | pub fn unbuffered(r: Reader) std.io.BufferedReader { | |
| 136 | return buffered(r, &.{}); | |
| 137 | } | |
| 138 | ||
| 133 | 139 | pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader { |
| 134 | 140 | return .{ |
| 135 | 141 | .reader = r, |
| ... | ... | @@ -140,10 +146,6 @@ pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader { |
| 140 | 146 | }; |
| 141 | 147 | } |
| 142 | 148 | |
| 143 | pub fn unbuffered(r: Reader) std.io.BufferedReader { | |
| 144 | return buffered(r, &.{}); | |
| 145 | } | |
| 146 | ||
| 147 | 149 | test "when the backing reader provides one byte at a time" { |
| 148 | 150 | const OneByteReader = struct { |
| 149 | 151 | str: []const u8, |
lib/std/io/Writer.zig+3-3| ... | ... | @@ -80,7 +80,7 @@ pub fn writeFile( |
| 80 | 80 | w: Writer, |
| 81 | 81 | file: std.fs.File, |
| 82 | 82 | offset: u64, |
| 83 | len: VTable.FileLen, | |
| 83 | len: FileLen, | |
| 84 | 84 | headers_and_trailers: []const []const u8, |
| 85 | 85 | headers_len: usize, |
| 86 | 86 | ) anyerror!usize { |
| ... | ... | @@ -91,7 +91,7 @@ pub fn unimplemented_writeFile( |
| 91 | 91 | context: *anyopaque, |
| 92 | 92 | file: std.fs.File, |
| 93 | 93 | offset: u64, |
| 94 | len: VTable.FileLen, | |
| 94 | len: FileLen, | |
| 95 | 95 | headers_and_trailers: []const []const u8, |
| 96 | 96 | headers_len: usize, |
| 97 | 97 | ) anyerror!usize { |
| ... | ... | @@ -107,7 +107,7 @@ pub fn unimplemented_writeFile( |
| 107 | 107 | pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter { |
| 108 | 108 | return .{ |
| 109 | 109 | .buffer = .initBuffer(buffer), |
| 110 | .mode = .{ .writer = w }, | |
| 110 | .unbuffered_writer = w, | |
| 111 | 111 | }; |
| 112 | 112 | } |
| 113 | 113 |
lib/std/net.zig+1-1| ... | ... | @@ -2011,7 +2011,7 @@ pub const Stream = struct { |
| 2011 | 2011 | context: *anyopaque, |
| 2012 | 2012 | in_file: std.fs.File, |
| 2013 | 2013 | in_offset: u64, |
| 2014 | in_len: std.io.Writer.VTable.FileLen, | |
| 2014 | in_len: std.io.Writer.FileLen, | |
| 2015 | 2015 | headers_and_trailers: []const []const u8, |
| 2016 | 2016 | headers_len: usize, |
| 2017 | 2017 | ) anyerror!usize { |