authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-29 23:11:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-30 09:26:34-07:00
logcf7a28febbbe877003d8d4f9a13ceb94698c1e3e
tree73f0874da561173692d69cccd25cc83de308f809
parenta9773944dc2b930facd66350dcbe87178aacf487

std.Io.Reader: introduce readVec back into the VTable

simplifies and fixes things addresses a subset of #24608

5 files changed, 313 insertions(+), 292 deletions(-)

lib/std/Io/Reader.zig+205-142
...@@ -43,8 +43,8 @@ pub const VTable = struct {...@@ -43,8 +43,8 @@ pub const VTable = struct {
43 ///43 ///
44 /// In addition to, or instead of writing to `w`, the implementation may44 /// In addition to, or instead of writing to `w`, the implementation may
45 /// choose to store data in `buffer`, modifying `seek` and `end`45 /// choose to store data in `buffer`, modifying `seek` and `end`
46 /// accordingly. Stream implementations are encouraged to take advantage of46 /// accordingly. Implementations are encouraged to take advantage of
47 /// this if simplifies the logic.47 /// this if it simplifies the logic.
48 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,48 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
4949
50 /// Consumes bytes from the internally tracked stream position without50 /// Consumes bytes from the internally tracked stream position without
...@@ -68,6 +68,21 @@ pub const VTable = struct {...@@ -68,6 +68,21 @@ pub const VTable = struct {
68 /// This function is only called when `buffer` is empty.68 /// This function is only called when `buffer` is empty.
69 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,69 discard: *const fn (r: *Reader, limit: Limit) Error!usize = defaultDiscard,
7070
71 /// Returns number of bytes written to `data`.
72 ///
73 /// `data` may not have nonzero length.
74 ///
75 /// `data` may not contain an alias to `Reader.buffer`.
76 ///
77 /// Implementations may ignore `data`, writing directly to `Reader.buffer`,
78 /// modifying `seek` and `end` accordingly, and returning 0 from this
79 /// function. Implementations are encouraged to take advantage of this if
80 /// it simplifies the logic.
81 ///
82 /// The default implementation calls `stream` with either `data[0]` or
83 /// `Reader.buffer`, whichever is bigger.
84 readVec: *const fn (r: *Reader, data: []const []u8) Error!usize = defaultReadVec,
85
71 /// Ensures `capacity` more data can be buffered without rebasing.86 /// Ensures `capacity` more data can be buffered without rebasing.
72 ///87 ///
73 /// Asserts `capacity` is within buffer capacity, or that the stream ends88 /// Asserts `capacity` is within buffer capacity, or that the stream ends
...@@ -138,6 +153,7 @@ pub fn fixed(buffer: []const u8) Reader {...@@ -138,6 +153,7 @@ pub fn fixed(buffer: []const u8) Reader {
138 .vtable = &.{153 .vtable = &.{
139 .stream = endingStream,154 .stream = endingStream,
140 .discard = endingDiscard,155 .discard = endingDiscard,
156 .readVec = endingReadVec,
141 .rebase = endingRebase,157 .rebase = endingRebase,
142 },158 },
143 // This cast is safe because all potential writes to it will instead159 // This cast is safe because all potential writes to it will instead
...@@ -170,18 +186,18 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize {...@@ -170,18 +186,18 @@ pub fn discard(r: *Reader, limit: Limit) Error!usize {
170 }186 }
171 break :l .limited(n - buffered_len);187 break :l .limited(n - buffered_len);
172 } else .unlimited;188 } else .unlimited;
173 r.seek = 0;189 r.seek = r.end;
174 r.end = 0;
175 const n = try r.vtable.discard(r, remaining);190 const n = try r.vtable.discard(r, remaining);
176 assert(n <= @intFromEnum(remaining));191 assert(n <= @intFromEnum(remaining));
177 return buffered_len + n;192 return buffered_len + n;
178}193}
179194
180pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {195pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
181 assert(r.seek == 0);196 assert(r.seek == r.end);
182 assert(r.end == 0);197 r.seek = 0;
183 var dw: Writer.Discarding = .init(r.buffer);198 r.end = 0;
184 const n = r.stream(&dw.writer, limit) catch |err| switch (err) {199 var d: Writer.Discarding = .init(r.buffer);
200 const n = r.stream(&d.writer, limit) catch |err| switch (err) {
185 error.WriteFailed => unreachable,201 error.WriteFailed => unreachable,
186 error.ReadFailed => return error.ReadFailed,202 error.ReadFailed => return error.ReadFailed,
187 error.EndOfStream => return error.EndOfStream,203 error.EndOfStream => return error.EndOfStream,
...@@ -294,7 +310,8 @@ pub fn appendRemaining(...@@ -294,7 +310,8 @@ pub fn appendRemaining(
294 list: *std.ArrayListAlignedUnmanaged(u8, alignment),310 list: *std.ArrayListAlignedUnmanaged(u8, alignment),
295 limit: Limit,311 limit: Limit,
296) LimitedAllocError!void {312) LimitedAllocError!void {
297 if (limit != .unlimited) assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.313 if (limit == .unlimited) return appendRemainingUnlimited(r, gpa, alignment, list, 1);
314 assert(r.buffer.len != 0); // Needed to detect limit exceeded without losing data.
298 const buffer_contents = r.buffer[r.seek..r.end];315 const buffer_contents = r.buffer[r.seek..r.end];
299 const copy_len = limit.minInt(buffer_contents.len);316 const copy_len = limit.minInt(buffer_contents.len);
300 try list.appendSlice(gpa, r.buffer[0..copy_len]);317 try list.appendSlice(gpa, r.buffer[0..copy_len]);
...@@ -303,32 +320,67 @@ pub fn appendRemaining(...@@ -303,32 +320,67 @@ pub fn appendRemaining(
303 r.seek = 0;320 r.seek = 0;
304 r.end = 0;321 r.end = 0;
305 var remaining = @intFromEnum(limit) - copy_len;322 var remaining = @intFromEnum(limit) - copy_len;
323 // From here, we leave `buffer` empty, appending directly to `list`.
324 var writer: Writer = .{
325 .buffer = undefined,
326 .end = undefined,
327 .vtable = &.{ .drain = Writer.fixedDrain },
328 };
306 while (true) {329 while (true) {
307 try list.ensureUnusedCapacity(gpa, 1);330 try list.ensureUnusedCapacity(gpa, 2);
308 const cap = list.unusedCapacitySlice();331 const cap = list.unusedCapacitySlice();
309 const dest = cap[0..@min(cap.len, remaining)];332 const dest = cap[0..@min(cap.len, remaining + 1)];
310 if (remaining - dest.len == 0) {333 writer.buffer = list.allocatedSlice();
311 // Additionally provides `buffer` to detect end.334 writer.end = list.items.len;
312 const new_remaining = readVecInner(r, &.{}, dest, remaining) catch |err| switch (err) {335 const n = r.vtable.stream(r, &writer, .limited(dest.len)) catch |err| switch (err) {
313 error.EndOfStream => {336 error.WriteFailed => unreachable, // Prevented by the limit.
314 if (r.bufferedLen() != 0) return error.StreamTooLong;337 error.EndOfStream => return,
315 return;338 error.ReadFailed => return error.ReadFailed,
316 },339 };
317 error.ReadFailed => return error.ReadFailed,340 list.items.len += n;
318 };341 if (n > remaining) {
319 list.items.len += remaining - new_remaining;342 // Move the byte to `Reader.buffer` so it is not lost.
320 remaining = new_remaining;343 assert(n - remaining == 1);
321 } else {344 assert(r.end == 0);
322 // Leave `buffer` empty, appending directly to `list`.345 r.buffer[0] = list.items[list.items.len - 1];
323 var dest_w: Writer = .fixed(dest);346 list.items.len -= 1;
324 const n = r.vtable.stream(r, &dest_w, .limited(dest.len)) catch |err| switch (err) {347 r.end = 1;
325 error.WriteFailed => unreachable, // Prevented by the limit.348 return;
326 error.EndOfStream => return,
327 error.ReadFailed => return error.ReadFailed,
328 };
329 list.items.len += n;
330 remaining -= n;
331 }349 }
350 remaining -= n;
351 }
352}
353
354pub const UnlimitedAllocError = Allocator.Error || ShortError;
355
356pub fn appendRemainingUnlimited(
357 r: *Reader,
358 gpa: Allocator,
359 comptime alignment: ?std.mem.Alignment,
360 list: *std.ArrayListAlignedUnmanaged(u8, alignment),
361 bump: usize,
362) UnlimitedAllocError!void {
363 const buffer_contents = r.buffer[r.seek..r.end];
364 try list.ensureUnusedCapacity(gpa, buffer_contents.len + bump);
365 list.appendSliceAssumeCapacity(buffer_contents);
366 r.seek = 0;
367 r.end = 0;
368 // From here, we leave `buffer` empty, appending directly to `list`.
369 var writer: Writer = .{
370 .buffer = undefined,
371 .end = undefined,
372 .vtable = &.{ .drain = Writer.fixedDrain },
373 };
374 while (true) {
375 try list.ensureUnusedCapacity(gpa, bump);
376 writer.buffer = list.allocatedSlice();
377 writer.end = list.items.len;
378 const n = r.vtable.stream(r, &writer, .limited(list.unusedCapacitySlice().len)) catch |err| switch (err) {
379 error.WriteFailed => unreachable, // Prevented by the limit.
380 error.EndOfStream => return,
381 error.ReadFailed => return error.ReadFailed,
382 };
383 list.items.len += n;
332 }384 }
333}385}
334386
...@@ -340,95 +392,64 @@ pub fn appendRemaining(...@@ -340,95 +392,64 @@ pub fn appendRemaining(
340///392///
341/// The reader's internal logical seek position moves forward in accordance393/// The reader's internal logical seek position moves forward in accordance
342/// with the number of bytes returned from this function.394/// with the number of bytes returned from this function.
343pub fn readVec(r: *Reader, data: []const []u8) Error!usize {395pub fn readVec(r: *Reader, data: [][]u8) Error!usize {
344 return readVecLimit(r, data, .unlimited);396 var seek = r.seek;
345}
346
347/// Equivalent to `readVec` but reads at most `limit` bytes.
348///
349/// This ultimately will lower to a call to `stream`, but it must ensure
350/// that the buffer used has at least as much capacity, in case that function
351/// depends on a minimum buffer capacity. It also ensures that if the `stream`
352/// implementation calls `Writer.writableVector`, it will get this data slice
353/// along with the buffer at the end.
354pub fn readVecLimit(r: *Reader, data: []const []u8, limit: Limit) Error!usize {
355 comptime assert(@intFromEnum(Limit.unlimited) == std.math.maxInt(usize));
356 var remaining = @intFromEnum(limit);
357 for (data, 0..) |buf, i| {397 for (data, 0..) |buf, i| {
358 const buffer_contents = r.buffer[r.seek..r.end];398 const contents = r.buffer[seek..r.end];
359 const copy_len = @min(buffer_contents.len, buf.len, remaining);399 const copy_len = @min(contents.len, buf.len);
360 @memcpy(buf[0..copy_len], buffer_contents[0..copy_len]);400 @memcpy(buf[0..copy_len], contents[0..copy_len]);
361 r.seek += copy_len;401 seek += copy_len;
362 remaining -= copy_len;
363 if (remaining == 0) break;
364 if (buf.len - copy_len == 0) continue;402 if (buf.len - copy_len == 0) continue;
365403
366 // All of `buffer` has been copied to `data`. We now set up a structure404 // All of `buffer` has been copied to `data`.
367 // that enables the `Writer.writableVector` API, while also ensuring405 const n = seek - r.seek;
368 // API that directly operates on the `Writable.buffer` has its minimum406 r.seek = seek;
369 // buffer capacity requirements met.407 data[i] = buf[copy_len..];
370 r.seek = 0;408 defer data[i] = buf;
371 r.end = 0;409 return n + try r.vtable.readVec(r, data[i..]);
372 remaining = try readVecInner(r, data[i + 1 ..], buf[copy_len..], remaining);
373 break;
374 }410 }
375 return @intFromEnum(limit) - remaining;411 const n = seek - r.seek;
412 r.seek = seek;
413 return n;
376}414}
377415
378fn readVecInner(r: *Reader, middle: []const []u8, first: []u8, remaining: usize) Error!usize {416/// Writes to `Reader.buffer` or `data`, whichever has larger capacity.
379 var wrapper: Writer.VectorWrapper = .{417pub fn defaultReadVec(r: *Reader, data: []const []u8) Error!usize {
380 .it = .{418 assert(r.seek == r.end);
381 .first = first,419 r.seek = 0;
382 .middle = middle,420 r.end = 0;
383 .last = r.buffer,421 const first = data[0];
384 },422 const direct = first.len >= r.buffer.len;
385 .writer = .{423 var writer: Writer = .{
386 .buffer = if (first.len >= r.buffer.len) first else r.buffer,424 .buffer = if (direct) first else r.buffer,
387 .vtable = Writer.VectorWrapper.vtable,425 .end = 0,
388 },426 .vtable = &.{ .drain = Writer.fixedDrain },
389 };427 };
390 // If the limit may pass beyond user buffer into Reader buffer, use428 const limit: Limit = .limited(writer.buffer.len - writer.end);
391 // unlimited, allowing the Reader buffer to fill.429 const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
392 const limit: Limit = l: {430 error.WriteFailed => unreachable,
393 var n: usize = first.len;431 else => |e| return e,
394 for (middle) |m| n += m.len;
395 break :l if (remaining >= n) .unlimited else .limited(remaining);
396 };432 };
397 var n = r.vtable.stream(r, &wrapper.writer, limit) catch |err| switch (err) {433 if (direct) return n;
398 error.WriteFailed => {434 r.end += n;
399 assert(!wrapper.used);435 return 0;
400 if (wrapper.writer.buffer.ptr == first.ptr) {436}
401 return remaining - wrapper.writer.end;437
402 } else {438/// Always writes to `Reader.buffer` and returns 0.
403 assert(wrapper.writer.end <= r.buffer.len);439pub fn indirectReadVec(r: *Reader, data: []const []u8) Error!usize {
404 r.end = wrapper.writer.end;440 _ = data;
405 return remaining;441 assert(r.seek == r.end);
406 }442 var writer: Writer = .{
407 },443 .buffer = r.buffer,
444 .end = r.end,
445 .vtable = &.{ .drain = Writer.fixedDrain },
446 };
447 const limit: Limit = .limited(writer.buffer.len - writer.end);
448 r.end += r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
449 error.WriteFailed => unreachable,
408 else => |e| return e,450 else => |e| return e,
409 };451 };
410 if (!wrapper.used) {452 return 0;
411 if (wrapper.writer.buffer.ptr == first.ptr) {
412 return remaining - n;
413 } else {
414 assert(n <= r.buffer.len);
415 r.end = n;
416 return remaining;
417 }
418 }
419 if (n < first.len) return remaining - n;
420 var result = remaining - first.len;
421 n -= first.len;
422 for (middle) |mid| {
423 if (n < mid.len) {
424 return result - n;
425 }
426 result -= mid.len;
427 n -= mid.len;
428 }
429 assert(n <= r.buffer.len);
430 r.end = n;
431 return result;
432}453}
433454
434pub fn buffered(r: *Reader) []u8 {455pub fn buffered(r: *Reader) []u8 {
...@@ -642,29 +663,24 @@ pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {...@@ -642,29 +663,24 @@ pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {
642/// See also:663/// See also:
643/// * `readSliceAll`664/// * `readSliceAll`
644pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {665pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize {
645 var i: usize = 0;666 const contents = r.buffer[r.seek..r.end];
667 const copy_len = @min(buffer.len, contents.len);
668 @memcpy(buffer[0..copy_len], contents[0..copy_len]);
669 r.seek += copy_len;
670 if (buffer.len - copy_len == 0) {
671 @branchHint(.likely);
672 return buffer.len;
673 }
674 var i: usize = copy_len;
675 var data: [1][]u8 = undefined;
646 while (true) {676 while (true) {
647 const buffer_contents = r.buffer[r.seek..r.end];677 data[0] = buffer[i..];
648 const dest = buffer[i..];678 i += readVec(r, &data) catch |err| switch (err) {
649 const copy_len = @min(dest.len, buffer_contents.len);
650 @memcpy(dest[0..copy_len], buffer_contents[0..copy_len]);
651 if (dest.len - copy_len == 0) {
652 @branchHint(.likely);
653 r.seek += copy_len;
654 return buffer.len;
655 }
656 i += copy_len;
657 r.end = 0;
658 r.seek = 0;
659 const remaining = buffer[i..];
660 const new_remaining_len = readVecInner(r, &.{}, remaining, remaining.len) catch |err| switch (err) {
661 error.EndOfStream => return i,679 error.EndOfStream => return i,
662 error.ReadFailed => return error.ReadFailed,680 error.ReadFailed => return error.ReadFailed,
663 };681 };
664 if (new_remaining_len == 0) return buffer.len;682 if (buffer.len - i == 0) return buffer.len;
665 i += remaining.len - new_remaining_len;
666 }683 }
667 return buffer.len;
668}684}
669685
670/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing686/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
...@@ -1632,19 +1648,6 @@ test readVec {...@@ -1632,19 +1648,6 @@ test readVec {
1632 try testing.expectEqualStrings(std.ascii.letters[26..], bufs[1]);1648 try testing.expectEqualStrings(std.ascii.letters[26..], bufs[1]);
1633}1649}
16341650
1635test readVecLimit {
1636 var r: Reader = .fixed(std.ascii.letters);
1637 var flat_buffer: [52]u8 = undefined;
1638 var bufs: [2][]u8 = .{
1639 flat_buffer[0..26],
1640 flat_buffer[26..],
1641 };
1642 // Short reads are possible with this function but not with fixed.
1643 try testing.expectEqual(50, try r.readVecLimit(&bufs, .limited(50)));
1644 try testing.expectEqualStrings(std.ascii.letters[0..26], bufs[0]);
1645 try testing.expectEqualStrings(std.ascii.letters[26..50], bufs[1][0..24]);
1646}
1647
1648test "expected error.EndOfStream" {1651test "expected error.EndOfStream" {
1649 // Unit test inspired by https://github.com/ziglang/zig/issues/177331652 // Unit test inspired by https://github.com/ziglang/zig/issues/17733
1650 var buffer: [3]u8 = undefined;1653 var buffer: [3]u8 = undefined;
...@@ -1661,6 +1664,12 @@ fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {...@@ -1661,6 +1664,12 @@ fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
1661 return error.EndOfStream;1664 return error.EndOfStream;
1662}1665}
16631666
1667fn endingReadVec(r: *Reader, data: []const []u8) Error!usize {
1668 _ = r;
1669 _ = data;
1670 return error.EndOfStream;
1671}
1672
1664fn endingDiscard(r: *Reader, limit: Limit) Error!usize {1673fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
1665 _ = r;1674 _ = r;
1666 _ = limit;1675 _ = limit;
...@@ -1797,3 +1806,57 @@ pub fn Hashed(comptime Hasher: type) type {...@@ -1797,3 +1806,57 @@ pub fn Hashed(comptime Hasher: type) type {
1797 }1806 }
1798 };1807 };
1799}1808}
1809
1810pub fn writableVectorPosix(r: *Reader, buffer: []std.posix.iovec, data: []const []u8) Error!struct { usize, usize } {
1811 var i: usize = 0;
1812 var n: usize = 0;
1813 for (data) |buf| {
1814 if (buffer.len - i == 0) return .{ i, n };
1815 if (buf.len != 0) {
1816 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
1817 i += 1;
1818 n += buf.len;
1819 }
1820 }
1821 assert(r.seek == r.end);
1822 const buf = r.buffer;
1823 if (buf.len != 0) {
1824 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
1825 i += 1;
1826 }
1827 return .{ i, n };
1828}
1829
1830pub fn writableVectorWsa(
1831 r: *Reader,
1832 buffer: []std.os.windows.ws2_32.WSABUF,
1833 data: []const []u8,
1834) Error!struct { usize, usize } {
1835 var i: usize = 0;
1836 var n: usize = 0;
1837 for (data) |buf| {
1838 if (buffer.len - i == 0) return .{ i, n };
1839 if (buf.len == 0) continue;
1840 if (std.math.cast(u32, buf.len)) |len| {
1841 buffer[i] = .{ .buf = buf.ptr, .len = len };
1842 i += 1;
1843 n += len;
1844 continue;
1845 }
1846 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
1847 i += 1;
1848 n += std.math.maxInt(u32);
1849 return .{ i, n };
1850 }
1851 assert(r.seek == r.end);
1852 const buf = r.buffer;
1853 if (buf.len != 0) {
1854 if (std.math.cast(u32, buf.len)) |len| {
1855 buffer[i] = .{ .buf = buf.ptr, .len = len };
1856 } else {
1857 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
1858 }
1859 i += 1;
1860 }
1861 return .{ i, n };
1862}
lib/std/Io/Writer.zig-98
...@@ -342,97 +342,6 @@ pub fn writableSlicePreserve(w: *Writer, preserve_len: usize, len: usize) Error!...@@ -342,97 +342,6 @@ pub fn writableSlicePreserve(w: *Writer, preserve_len: usize, len: usize) Error!
342 return big_slice[0..len];342 return big_slice[0..len];
343}343}
344344
345pub const WritableVectorIterator = struct {
346 first: []u8,
347 middle: []const []u8 = &.{},
348 last: []u8 = &.{},
349 index: usize = 0,
350
351 pub fn next(it: *WritableVectorIterator) ?[]u8 {
352 while (true) {
353 const i = it.index;
354 it.index += 1;
355 if (i == 0) {
356 if (it.first.len == 0) continue;
357 return it.first;
358 }
359 const middle_index = i - 1;
360 if (middle_index < it.middle.len) {
361 const middle = it.middle[middle_index];
362 if (middle.len == 0) continue;
363 return middle;
364 }
365 if (middle_index == it.middle.len) {
366 if (it.last.len == 0) continue;
367 return it.last;
368 }
369 return null;
370 }
371 }
372};
373
374pub const VectorWrapper = struct {
375 writer: Writer,
376 it: WritableVectorIterator,
377 /// Tracks whether the "writable vector" API was used.
378 used: bool = false,
379 pub const vtable: *const VTable = &unique_vtable_allocation;
380 /// This is intended to be constant but it must be a unique address for
381 /// `@fieldParentPtr` to work.
382 var unique_vtable_allocation: VTable = .{ .drain = fixedDrain };
383};
384
385pub fn writableVectorIterator(w: *Writer) Error!WritableVectorIterator {
386 if (w.vtable == VectorWrapper.vtable) {
387 const wrapper: *VectorWrapper = @fieldParentPtr("writer", w);
388 wrapper.used = true;
389 return wrapper.it;
390 }
391 return .{ .first = try writableSliceGreedy(w, 1) };
392}
393
394pub fn writableVectorPosix(w: *Writer, buffer: []std.posix.iovec, limit: Limit) Error![]std.posix.iovec {
395 var it = try writableVectorIterator(w);
396 var i: usize = 0;
397 var remaining = limit;
398 while (it.next()) |full_buffer| {
399 if (!remaining.nonzero()) break;
400 if (buffer.len - i == 0) break;
401 const buf = remaining.slice(full_buffer);
402 if (buf.len == 0) continue;
403 buffer[i] = .{ .base = buf.ptr, .len = buf.len };
404 i += 1;
405 remaining = remaining.subtract(buf.len).?;
406 }
407 return buffer[0..i];
408}
409
410pub fn writableVectorWsa(
411 w: *Writer,
412 buffer: []std.os.windows.ws2_32.WSABUF,
413 limit: Limit,
414) Error![]std.os.windows.ws2_32.WSABUF {
415 var it = try writableVectorIterator(w);
416 var i: usize = 0;
417 var remaining = limit;
418 while (it.next()) |full_buffer| {
419 if (!remaining.nonzero()) break;
420 if (buffer.len - i == 0) break;
421 const buf = remaining.slice(full_buffer);
422 if (buf.len == 0) continue;
423 if (std.math.cast(u32, buf.len)) |len| {
424 buffer[i] = .{ .buf = buf.ptr, .len = len };
425 i += 1;
426 remaining = remaining.subtract(len).?;
427 continue;
428 }
429 buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
430 i += 1;
431 break;
432 }
433 return buffer[0..i];
434}
435
436pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {345pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
437 _ = try writableSliceGreedy(w, n);346 _ = try writableSliceGreedy(w, n);
438}347}
...@@ -451,13 +360,6 @@ pub fn advance(w: *Writer, n: usize) void {...@@ -451,13 +360,6 @@ pub fn advance(w: *Writer, n: usize) void {
451 w.end = new_end;360 w.end = new_end;
452}361}
453362
454/// After calling `writableVector`, this function tracks how many bytes were
455/// written to it.
456pub fn advanceVector(w: *Writer, n: usize) usize {
457 if (w.vtable != VectorWrapper.vtable) advance(w, n);
458 return n;
459}
460
461/// The `data` parameter is mutable because this function needs to mutate the363/// The `data` parameter is mutable because this function needs to mutate the
462/// fields in order to handle partial writes from `VTable.writeSplat`.364/// fields in order to handle partial writes from `VTable.writeSplat`.
463pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {365pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void {
lib/std/compress/zstd/Decompress.zig+17-3
...@@ -88,6 +88,8 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {...@@ -88,6 +88,8 @@ pub fn init(input: *Reader, buffer: []u8, options: Options) Decompress {
88 .vtable = &.{88 .vtable = &.{
89 .stream = stream,89 .stream = stream,
90 .rebase = rebase,90 .rebase = rebase,
91 .discard = discard,
92 .readVec = Reader.indirectReadVec,
91 },93 },
92 .buffer = buffer,94 .buffer = buffer,
93 .seek = 0,95 .seek = 0,
...@@ -100,11 +102,23 @@ fn rebase(r: *Reader, capacity: usize) Reader.RebaseError!void {...@@ -100,11 +102,23 @@ fn rebase(r: *Reader, capacity: usize) Reader.RebaseError!void {
100 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));102 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
101 assert(capacity <= r.buffer.len - d.window_len);103 assert(capacity <= r.buffer.len - d.window_len);
102 assert(r.end + capacity > r.buffer.len);104 assert(r.end + capacity > r.buffer.len);
103 const discard = r.end - d.window_len;105 const discard_n = r.end - d.window_len;
104 const keep = r.buffer[discard..r.end];106 const keep = r.buffer[discard_n..r.end];
105 @memmove(r.buffer[0..keep.len], keep);107 @memmove(r.buffer[0..keep.len], keep);
106 r.end = keep.len;108 r.end = keep.len;
107 r.seek -= discard;109 r.seek -= discard_n;
110}
111
112fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
113 r.rebase(zstd.block_size_max) catch unreachable;
114 var d: Writer.Discarding = .init(r.buffer);
115 const n = r.stream(&d.writer, limit) catch |err| switch (err) {
116 error.WriteFailed => unreachable,
117 error.ReadFailed => return error.ReadFailed,
118 error.EndOfStream => return error.EndOfStream,
119 };
120 assert(n <= @intFromEnum(limit));
121 return n;
108}122}
109123
110fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {124fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
lib/std/fs/File.zig+57-31
...@@ -1129,7 +1129,7 @@ pub fn seekableStream(file: File) SeekableStream {...@@ -1129,7 +1129,7 @@ pub fn seekableStream(file: File) SeekableStream {
1129/// * Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`)1129/// * Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`)
1130/// versus plain variants (e.g. `read`).1130/// versus plain variants (e.g. `read`).
1131///1131///
1132/// Fulfills the `std.io.Reader` interface.1132/// Fulfills the `std.Io.Reader` interface.
1133pub const Reader = struct {1133pub const Reader = struct {
1134 file: File,1134 file: File,
1135 err: ?ReadError = null,1135 err: ?ReadError = null,
...@@ -1140,7 +1140,7 @@ pub const Reader = struct {...@@ -1140,7 +1140,7 @@ pub const Reader = struct {
1140 size: ?u64 = null,1140 size: ?u64 = null,
1141 size_err: ?GetEndPosError = null,1141 size_err: ?GetEndPosError = null,
1142 seek_err: ?Reader.SeekError = null,1142 seek_err: ?Reader.SeekError = null,
1143 interface: std.io.Reader,1143 interface: std.Io.Reader,
11441144
1145 pub const SeekError = File.SeekError || error{1145 pub const SeekError = File.SeekError || error{
1146 /// Seeking fell back to reading, and reached the end before the requested seek position.1146 /// Seeking fell back to reading, and reached the end before the requested seek position.
...@@ -1177,11 +1177,12 @@ pub const Reader = struct {...@@ -1177,11 +1177,12 @@ pub const Reader = struct {
1177 }1177 }
1178 };1178 };
11791179
1180 pub fn initInterface(buffer: []u8) std.io.Reader {1180 pub fn initInterface(buffer: []u8) std.Io.Reader {
1181 return .{1181 return .{
1182 .vtable = &.{1182 .vtable = &.{
1183 .stream = Reader.stream,1183 .stream = Reader.stream,
1184 .discard = Reader.discard,1184 .discard = Reader.discard,
1185 .readVec = Reader.readVec,
1185 },1186 },
1186 .buffer = buffer,1187 .buffer = buffer,
1187 .seek = 0,1188 .seek = 0,
...@@ -1294,7 +1295,7 @@ pub const Reader = struct {...@@ -1294,7 +1295,7 @@ pub const Reader = struct {
1294 /// vectors through the underlying read calls as possible.1295 /// vectors through the underlying read calls as possible.
1295 const max_buffers_len = 16;1296 const max_buffers_len = 16;
12961297
1297 fn stream(io_reader: *std.io.Reader, w: *std.io.Writer, limit: std.io.Limit) std.io.Reader.StreamError!usize {1298 fn stream(io_reader: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
1298 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));1299 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
1299 switch (r.mode) {1300 switch (r.mode) {
1300 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {1301 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
...@@ -1305,16 +1306,33 @@ pub const Reader = struct {...@@ -1305,16 +1306,33 @@ pub const Reader = struct {
1305 else => |e| return e,1306 else => |e| return e,
1306 },1307 },
1307 .positional_reading => {1308 .positional_reading => {
1309 const dest = limit.slice(try w.writableSliceGreedy(1));
1310 const n = try readPositional(r, dest);
1311 w.advance(n);
1312 return n;
1313 },
1314 .streaming_reading => {
1315 const dest = limit.slice(try w.writableSliceGreedy(1));
1316 const n = try readStreaming(r, dest);
1317 w.advance(n);
1318 return n;
1319 },
1320 .failure => return error.ReadFailed,
1321 }
1322 }
1323
1324 fn readVec(io_reader: *std.Io.Reader, data: []const []u8) std.Io.Reader.Error!usize {
1325 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
1326 switch (r.mode) {
1327 .positional, .positional_reading => {
1308 if (is_windows) {1328 if (is_windows) {
1309 // Unfortunately, `ReadFileScatter` cannot be used since it1329 // Unfortunately, `ReadFileScatter` cannot be used since it
1310 // requires page alignment.1330 // requires page alignment.
1311 const dest = limit.slice(try w.writableSliceGreedy(1));1331 return readPositional(r, data[0]);
1312 const n = try readPositional(r, dest);
1313 w.advance(n);
1314 return n;
1315 }1332 }
1316 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;1333 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1317 const dest = try w.writableVectorPosix(&iovecs_buffer, limit);1334 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
1335 const dest = iovecs_buffer[0..dest_n];
1318 assert(dest[0].len > 0);1336 assert(dest[0].len > 0);
1319 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {1337 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
1320 error.Unseekable => {1338 error.Unseekable => {
...@@ -1339,19 +1357,22 @@ pub const Reader = struct {...@@ -1339,19 +1357,22 @@ pub const Reader = struct {
1339 return error.EndOfStream;1357 return error.EndOfStream;
1340 }1358 }
1341 r.pos += n;1359 r.pos += n;
1342 return w.advanceVector(n);1360 if (n > data_size) {
1361 io_reader.seek = 0;
1362 io_reader.end = n - data_size;
1363 return data_size;
1364 }
1365 return n;
1343 },1366 },
1344 .streaming_reading => {1367 .streaming, .streaming_reading => {
1345 if (is_windows) {1368 if (is_windows) {
1346 // Unfortunately, `ReadFileScatter` cannot be used since it1369 // Unfortunately, `ReadFileScatter` cannot be used since it
1347 // requires page alignment.1370 // requires page alignment.
1348 const dest = limit.slice(try w.writableSliceGreedy(1));1371 return readStreaming(r, data[0]);
1349 const n = try readStreaming(r, dest);
1350 w.advance(n);
1351 return n;
1352 }1372 }
1353 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;1373 var iovecs_buffer: [max_buffers_len]posix.iovec = undefined;
1354 const dest = try w.writableVectorPosix(&iovecs_buffer, limit);1374 const dest_n, const data_size = try io_reader.writableVectorPosix(&iovecs_buffer, data);
1375 const dest = iovecs_buffer[0..dest_n];
1355 assert(dest[0].len > 0);1376 assert(dest[0].len > 0);
1356 const n = posix.readv(r.file.handle, dest) catch |err| {1377 const n = posix.readv(r.file.handle, dest) catch |err| {
1357 r.err = err;1378 r.err = err;
...@@ -1362,13 +1383,18 @@ pub const Reader = struct {...@@ -1362,13 +1383,18 @@ pub const Reader = struct {
1362 return error.EndOfStream;1383 return error.EndOfStream;
1363 }1384 }
1364 r.pos += n;1385 r.pos += n;
1365 return w.advanceVector(n);1386 if (n > data_size) {
1387 io_reader.seek = 0;
1388 io_reader.end = n - data_size;
1389 return data_size;
1390 }
1391 return n;
1366 },1392 },
1367 .failure => return error.ReadFailed,1393 .failure => return error.ReadFailed,
1368 }1394 }
1369 }1395 }
13701396
1371 fn discard(io_reader: *std.io.Reader, limit: std.io.Limit) std.io.Reader.Error!usize {1397 fn discard(io_reader: *std.Io.Reader, limit: std.Io.Limit) std.Io.Reader.Error!usize {
1372 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));1398 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
1373 const file = r.file;1399 const file = r.file;
1374 const pos = r.pos;1400 const pos = r.pos;
...@@ -1447,7 +1473,7 @@ pub const Reader = struct {...@@ -1447,7 +1473,7 @@ pub const Reader = struct {
1447 }1473 }
1448 }1474 }
14491475
1450 pub fn readPositional(r: *Reader, dest: []u8) std.io.Reader.Error!usize {1476 pub fn readPositional(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1451 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {1477 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
1452 error.Unseekable => {1478 error.Unseekable => {
1453 r.mode = r.mode.toStreaming();1479 r.mode = r.mode.toStreaming();
...@@ -1474,7 +1500,7 @@ pub const Reader = struct {...@@ -1474,7 +1500,7 @@ pub const Reader = struct {
1474 return n;1500 return n;
1475 }1501 }
14761502
1477 pub fn readStreaming(r: *Reader, dest: []u8) std.io.Reader.Error!usize {1503 pub fn readStreaming(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1478 const n = r.file.read(dest) catch |err| {1504 const n = r.file.read(dest) catch |err| {
1479 r.err = err;1505 r.err = err;
1480 return error.ReadFailed;1506 return error.ReadFailed;
...@@ -1487,7 +1513,7 @@ pub const Reader = struct {...@@ -1487,7 +1513,7 @@ pub const Reader = struct {
1487 return n;1513 return n;
1488 }1514 }
14891515
1490 pub fn read(r: *Reader, dest: []u8) std.io.Reader.Error!usize {1516 pub fn read(r: *Reader, dest: []u8) std.Io.Reader.Error!usize {
1491 switch (r.mode) {1517 switch (r.mode) {
1492 .positional, .positional_reading => return readPositional(r, dest),1518 .positional, .positional_reading => return readPositional(r, dest),
1493 .streaming, .streaming_reading => return readStreaming(r, dest),1519 .streaming, .streaming_reading => return readStreaming(r, dest),
...@@ -1513,7 +1539,7 @@ pub const Writer = struct {...@@ -1513,7 +1539,7 @@ pub const Writer = struct {
1513 copy_file_range_err: ?CopyFileRangeError = null,1539 copy_file_range_err: ?CopyFileRangeError = null,
1514 fcopyfile_err: ?FcopyfileError = null,1540 fcopyfile_err: ?FcopyfileError = null,
1515 seek_err: ?SeekError = null,1541 seek_err: ?SeekError = null,
1516 interface: std.io.Writer,1542 interface: std.Io.Writer,
15171543
1518 pub const Mode = Reader.Mode;1544 pub const Mode = Reader.Mode;
15191545
...@@ -1550,13 +1576,13 @@ pub const Writer = struct {...@@ -1550,13 +1576,13 @@ pub const Writer = struct {
1550 };1576 };
1551 }1577 }
15521578
1553 pub fn initInterface(buffer: []u8) std.io.Writer {1579 pub fn initInterface(buffer: []u8) std.Io.Writer {
1554 return .{1580 return .{
1555 .vtable = &.{1581 .vtable = &.{
1556 .drain = drain,1582 .drain = drain,
1557 .sendFile = switch (builtin.zig_backend) {1583 .sendFile = switch (builtin.zig_backend) {
1558 else => sendFile,1584 else => sendFile,
1559 .stage2_aarch64 => std.io.Writer.unimplementedSendFile,1585 .stage2_aarch64 => std.Io.Writer.unimplementedSendFile,
1560 },1586 },
1561 },1587 },
1562 .buffer = buffer,1588 .buffer = buffer,
...@@ -1574,7 +1600,7 @@ pub const Writer = struct {...@@ -1574,7 +1600,7 @@ pub const Writer = struct {
1574 };1600 };
1575 }1601 }
15761602
1577 pub fn drain(io_w: *std.io.Writer, data: []const []const u8, splat: usize) std.io.Writer.Error!usize {1603 pub fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
1578 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));1604 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
1579 const handle = w.file.handle;1605 const handle = w.file.handle;
1580 const buffered = io_w.buffered();1606 const buffered = io_w.buffered();
...@@ -1724,10 +1750,10 @@ pub const Writer = struct {...@@ -1724,10 +1750,10 @@ pub const Writer = struct {
1724 }1750 }
17251751
1726 pub fn sendFile(1752 pub fn sendFile(
1727 io_w: *std.io.Writer,1753 io_w: *std.Io.Writer,
1728 file_reader: *Reader,1754 file_reader: *Reader,
1729 limit: std.io.Limit,1755 limit: std.Io.Limit,
1730 ) std.io.Writer.FileError!usize {1756 ) std.Io.Writer.FileError!usize {
1731 const reader_buffered = file_reader.interface.buffered();1757 const reader_buffered = file_reader.interface.buffered();
1732 if (reader_buffered.len >= @intFromEnum(limit))1758 if (reader_buffered.len >= @intFromEnum(limit))
1733 return sendFileBuffered(io_w, file_reader, reader_buffered);1759 return sendFileBuffered(io_w, file_reader, reader_buffered);
...@@ -1989,10 +2015,10 @@ pub const Writer = struct {...@@ -1989,10 +2015,10 @@ pub const Writer = struct {
1989 }2015 }
19902016
1991 fn sendFileBuffered(2017 fn sendFileBuffered(
1992 io_w: *std.io.Writer,2018 io_w: *std.Io.Writer,
1993 file_reader: *Reader,2019 file_reader: *Reader,
1994 reader_buffered: []const u8,2020 reader_buffered: []const u8,
1995 ) std.io.Writer.FileError!usize {2021 ) std.Io.Writer.FileError!usize {
1996 const n = try drain(io_w, &.{reader_buffered}, 1);2022 const n = try drain(io_w, &.{reader_buffered}, 1);
1997 file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;2023 file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
1998 return n;2024 return n;
...@@ -2015,7 +2041,7 @@ pub const Writer = struct {...@@ -2015,7 +2041,7 @@ pub const Writer = struct {
2015 }2041 }
2016 }2042 }
20172043
2018 pub const EndError = SetEndPosError || std.io.Writer.Error;2044 pub const EndError = SetEndPosError || std.Io.Writer.Error;
20192045
2020 /// Flushes any buffered data and sets the end position of the file.2046 /// Flushes any buffered data and sets the end position of the file.
2021 ///2047 ///
lib/std/net.zig+34-18
...@@ -7,7 +7,7 @@ const net = @This();...@@ -7,7 +7,7 @@ const net = @This();
7const mem = std.mem;7const mem = std.mem;
8const posix = std.posix;8const posix = std.posix;
9const fs = std.fs;9const fs = std.fs;
10const io = std.io;10const Io = std.Io;
11const native_endian = builtin.target.cpu.arch.endian();11const native_endian = builtin.target.cpu.arch.endian();
12const native_os = builtin.os.tag;12const native_os = builtin.os.tag;
13const windows = std.os.windows;13const windows = std.os.windows;
...@@ -165,7 +165,7 @@ pub const Address = extern union {...@@ -165,7 +165,7 @@ pub const Address = extern union {
165 }165 }
166 }166 }
167167
168 pub fn format(self: Address, w: *std.io.Writer) std.io.Writer.Error!void {168 pub fn format(self: Address, w: *Io.Writer) Io.Writer.Error!void {
169 switch (self.any.family) {169 switch (self.any.family) {
170 posix.AF.INET => try self.in.format(w),170 posix.AF.INET => try self.in.format(w),
171 posix.AF.INET6 => try self.in6.format(w),171 posix.AF.INET6 => try self.in6.format(w),
...@@ -342,7 +342,7 @@ pub const Ip4Address = extern struct {...@@ -342,7 +342,7 @@ pub const Ip4Address = extern struct {
342 self.sa.port = mem.nativeToBig(u16, port);342 self.sa.port = mem.nativeToBig(u16, port);
343 }343 }
344344
345 pub fn format(self: Ip4Address, w: *std.io.Writer) std.io.Writer.Error!void {345 pub fn format(self: Ip4Address, w: *Io.Writer) Io.Writer.Error!void {
346 const bytes: *const [4]u8 = @ptrCast(&self.sa.addr);346 const bytes: *const [4]u8 = @ptrCast(&self.sa.addr);
347 try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() });347 try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() });
348 }348 }
...@@ -633,7 +633,7 @@ pub const Ip6Address = extern struct {...@@ -633,7 +633,7 @@ pub const Ip6Address = extern struct {
633 self.sa.port = mem.nativeToBig(u16, port);633 self.sa.port = mem.nativeToBig(u16, port);
634 }634 }
635635
636 pub fn format(self: Ip6Address, w: *std.io.Writer) std.io.Writer.Error!void {636 pub fn format(self: Ip6Address, w: *Io.Writer) Io.Writer.Error!void {
637 const port = mem.bigToNative(u16, self.sa.port);637 const port = mem.bigToNative(u16, self.sa.port);
638 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {638 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
639 try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{639 try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{
...@@ -1348,7 +1348,7 @@ fn parseHosts(...@@ -1348,7 +1348,7 @@ fn parseHosts(
1348 name: []const u8,1348 name: []const u8,
1349 family: posix.sa_family_t,1349 family: posix.sa_family_t,
1350 port: u16,1350 port: u16,
1351 br: *io.Reader,1351 br: *Io.Reader,
1352) error{ OutOfMemory, ReadFailed }!void {1352) error{ OutOfMemory, ReadFailed }!void {
1353 while (true) {1353 while (true) {
1354 const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) {1354 const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) {
...@@ -1402,7 +1402,7 @@ test parseHosts {...@@ -1402,7 +1402,7 @@ test parseHosts {
1402 // TODO parsing addresses should not have OS dependencies1402 // TODO parsing addresses should not have OS dependencies
1403 return error.SkipZigTest;1403 return error.SkipZigTest;
1404 }1404 }
1405 var reader: std.io.Reader = .fixed(1405 var reader: Io.Reader = .fixed(
1406 \\127.0.0.1 localhost1406 \\127.0.0.1 localhost
1407 \\::1 localhost1407 \\::1 localhost
1408 \\127.0.0.2 abcd1408 \\127.0.0.2 abcd
...@@ -1583,7 +1583,7 @@ const ResolvConf = struct {...@@ -1583,7 +1583,7 @@ const ResolvConf = struct {
1583 const Directive = enum { options, nameserver, domain, search };1583 const Directive = enum { options, nameserver, domain, search };
1584 const Option = enum { ndots, attempts, timeout };1584 const Option = enum { ndots, attempts, timeout };
15851585
1586 fn parse(rc: *ResolvConf, reader: *io.Reader) !void {1586 fn parse(rc: *ResolvConf, reader: *Io.Reader) !void {
1587 const gpa = rc.gpa;1587 const gpa = rc.gpa;
1588 while (reader.takeSentinel('\n')) |line_with_comment| {1588 while (reader.takeSentinel('\n')) |line_with_comment| {
1589 const line = line: {1589 const line = line: {
...@@ -1894,7 +1894,7 @@ pub const Stream = struct {...@@ -1894,7 +1894,7 @@ pub const Stream = struct {
1894 pub const Reader = switch (native_os) {1894 pub const Reader = switch (native_os) {
1895 .windows => struct {1895 .windows => struct {
1896 /// Use `interface` for portable code.1896 /// Use `interface` for portable code.
1897 interface_state: io.Reader,1897 interface_state: Io.Reader,
1898 /// Use `getStream` for portable code.1898 /// Use `getStream` for portable code.
1899 net_stream: Stream,1899 net_stream: Stream,
1900 /// Use `getError` for portable code.1900 /// Use `getError` for portable code.
...@@ -1910,14 +1910,17 @@ pub const Stream = struct {...@@ -1910,14 +1910,17 @@ pub const Stream = struct {
1910 return r.error_state;1910 return r.error_state;
1911 }1911 }
19121912
1913 pub fn interface(r: *Reader) *io.Reader {1913 pub fn interface(r: *Reader) *Io.Reader {
1914 return &r.interface_state;1914 return &r.interface_state;
1915 }1915 }
19161916
1917 pub fn init(net_stream: Stream, buffer: []u8) Reader {1917 pub fn init(net_stream: Stream, buffer: []u8) Reader {
1918 return .{1918 return .{
1919 .interface_state = .{1919 .interface_state = .{
1920 .vtable = &.{ .stream = stream },1920 .vtable = &.{
1921 .stream = stream,
1922 .readVec = readVec,
1923 },
1921 .buffer = buffer,1924 .buffer = buffer,
1922 .seek = 0,1925 .seek = 0,
1923 .end = 0,1926 .end = 0,
...@@ -1927,16 +1930,29 @@ pub const Stream = struct {...@@ -1927,16 +1930,29 @@ pub const Stream = struct {
1927 };1930 };
1928 }1931 }
19291932
1930 fn stream(io_r: *io.Reader, io_w: *io.Writer, limit: io.Limit) io.Reader.StreamError!usize {1933 fn stream(io_r: *Io.Reader, io_w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
1934 const dest = limit.slice(try io_w.writableSliceGreedy(1));
1935 const n = try readVec(io_r, &.{dest});
1936 io_w.advance(n);
1937 return n;
1938 }
1939
1940 fn readVec(io_r: *std.Io.Reader, data: []const []u8) Io.Reader.Error!usize {
1931 const r: *Reader = @alignCast(@fieldParentPtr("interface_state", io_r));1941 const r: *Reader = @alignCast(@fieldParentPtr("interface_state", io_r));
1932 var iovecs: [max_buffers_len]windows.ws2_32.WSABUF = undefined;1942 var iovecs: [max_buffers_len]windows.ws2_32.WSABUF = undefined;
1933 const bufs = try io_w.writableVectorWsa(&iovecs, limit);1943 const bufs_n, const data_size = try io_r.writableVectorWsa(&iovecs, data);
1944 const bufs = iovecs[0..bufs_n];
1934 assert(bufs[0].len != 0);1945 assert(bufs[0].len != 0);
1935 const n = streamBufs(r, bufs) catch |err| {1946 const n = streamBufs(r, bufs) catch |err| {
1936 r.error_state = err;1947 r.error_state = err;
1937 return error.ReadFailed;1948 return error.ReadFailed;
1938 };1949 };
1939 if (n == 0) return error.EndOfStream;1950 if (n == 0) return error.EndOfStream;
1951 if (n > data_size) {
1952 io_r.seek = 0;
1953 io_r.end = n - data_size;
1954 return data_size;
1955 }
1940 return n;1956 return n;
1941 }1957 }
19421958
...@@ -1968,7 +1984,7 @@ pub const Stream = struct {...@@ -1968,7 +1984,7 @@ pub const Stream = struct {
19681984
1969 pub const Error = ReadError;1985 pub const Error = ReadError;
19701986
1971 pub fn interface(r: *Reader) *io.Reader {1987 pub fn interface(r: *Reader) *Io.Reader {
1972 return &r.file_reader.interface;1988 return &r.file_reader.interface;
1973 }1989 }
19741990
...@@ -1996,7 +2012,7 @@ pub const Stream = struct {...@@ -1996,7 +2012,7 @@ pub const Stream = struct {
1996 pub const Writer = switch (native_os) {2012 pub const Writer = switch (native_os) {
1997 .windows => struct {2013 .windows => struct {
1998 /// This field is present on all systems.2014 /// This field is present on all systems.
1999 interface: io.Writer,2015 interface: Io.Writer,
2000 /// Use `getStream` for cross-platform support.2016 /// Use `getStream` for cross-platform support.
2001 stream: Stream,2017 stream: Stream,
2002 /// This field is present on all systems.2018 /// This field is present on all systems.
...@@ -2034,7 +2050,7 @@ pub const Stream = struct {...@@ -2034,7 +2050,7 @@ pub const Stream = struct {
2034 }2050 }
2035 }2051 }
20362052
2037 fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {2053 fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
2038 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));2054 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
2039 const buffered = io_w.buffered();2055 const buffered = io_w.buffered();
2040 comptime assert(native_os == .windows);2056 comptime assert(native_os == .windows);
...@@ -2106,7 +2122,7 @@ pub const Stream = struct {...@@ -2106,7 +2122,7 @@ pub const Stream = struct {
2106 },2122 },
2107 else => struct {2123 else => struct {
2108 /// This field is present on all systems.2124 /// This field is present on all systems.
2109 interface: io.Writer,2125 interface: Io.Writer,
21102126
2111 err: ?Error = null,2127 err: ?Error = null,
2112 file_writer: File.Writer,2128 file_writer: File.Writer,
...@@ -2138,7 +2154,7 @@ pub const Stream = struct {...@@ -2138,7 +2154,7 @@ pub const Stream = struct {
2138 i.* += 1;2154 i.* += 1;
2139 }2155 }
21402156
2141 fn drain(io_w: *io.Writer, data: []const []const u8, splat: usize) io.Writer.Error!usize {2157 fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
2142 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));2158 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
2143 const buffered = io_w.buffered();2159 const buffered = io_w.buffered();
2144 var iovecs: [max_buffers_len]posix.iovec_const = undefined;2160 var iovecs: [max_buffers_len]posix.iovec_const = undefined;
...@@ -2190,7 +2206,7 @@ pub const Stream = struct {...@@ -2190,7 +2206,7 @@ pub const Stream = struct {
2190 });2206 });
2191 }2207 }
21922208
2193 fn sendFile(io_w: *io.Writer, file_reader: *File.Reader, limit: io.Limit) io.Writer.FileError!usize {2209 fn sendFile(io_w: *Io.Writer, file_reader: *File.Reader, limit: Io.Limit) Io.Writer.FileError!usize {
2194 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));2210 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
2195 const n = try w.file_writer.interface.sendFileHeader(io_w.buffered(), file_reader, limit);2211 const n = try w.file_writer.interface.sendFileHeader(io_w.buffered(), file_reader, limit);
2196 return io_w.consume(n);2212 return io_w.consume(n);