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 {
4343 ///
4444 /// In addition to, or instead of writing to `w`, the implementation may
4545 /// choose to store data in `buffer`, modifying `seek` and `end`
46 /// accordingly. Stream implementations are encouraged to take advantage of
47 /// this if simplifies the logic.
46 /// accordingly. Implementations are encouraged to take advantage of
47 /// this if it simplifies the logic.
4848 stream: *const fn (r: *Reader, w: *Writer, limit: Limit) StreamError!usize,
4949
5050 /// Consumes bytes from the internally tracked stream position without
......@@ -68,6 +68,21 @@ pub const VTable = struct {
6868 /// This function is only called when `buffer` is empty.
6969 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
7186 /// Ensures `capacity` more data can be buffered without rebasing.
7287 ///
7388 /// Asserts `capacity` is within buffer capacity, or that the stream ends
......@@ -138,6 +153,7 @@ pub fn fixed(buffer: []const u8) Reader {
138153 .vtable = &.{
139154 .stream = endingStream,
140155 .discard = endingDiscard,
156 .readVec = endingReadVec,
141157 .rebase = endingRebase,
142158 },
143159 // 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 {
170186 }
171187 break :l .limited(n - buffered_len);
172188 } else .unlimited;
173 r.seek = 0;
174 r.end = 0;
189 r.seek = r.end;
175190 const n = try r.vtable.discard(r, remaining);
176191 assert(n <= @intFromEnum(remaining));
177192 return buffered_len + n;
178193}
179194
180195pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize {
181 assert(r.seek == 0);
182 assert(r.end == 0);
183 var dw: Writer.Discarding = .init(r.buffer);
184 const n = r.stream(&dw.writer, limit) catch |err| switch (err) {
196 assert(r.seek == r.end);
197 r.seek = 0;
198 r.end = 0;
199 var d: Writer.Discarding = .init(r.buffer);
200 const n = r.stream(&d.writer, limit) catch |err| switch (err) {
185201 error.WriteFailed => unreachable,
186202 error.ReadFailed => return error.ReadFailed,
187203 error.EndOfStream => return error.EndOfStream,
......@@ -294,7 +310,8 @@ pub fn appendRemaining(
294310 list: *std.ArrayListAlignedUnmanaged(u8, alignment),
295311 limit: Limit,
296312) 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.
298315 const buffer_contents = r.buffer[r.seek..r.end];
299316 const copy_len = limit.minInt(buffer_contents.len);
300317 try list.appendSlice(gpa, r.buffer[0..copy_len]);
......@@ -303,32 +320,67 @@ pub fn appendRemaining(
303320 r.seek = 0;
304321 r.end = 0;
305322 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 };
306329 while (true) {
307 try list.ensureUnusedCapacity(gpa, 1);
330 try list.ensureUnusedCapacity(gpa, 2);
308331 const cap = list.unusedCapacitySlice();
309 const dest = cap[0..@min(cap.len, remaining)];
310 if (remaining - dest.len == 0) {
311 // Additionally provides `buffer` to detect end.
312 const new_remaining = readVecInner(r, &.{}, dest, remaining) catch |err| switch (err) {
313 error.EndOfStream => {
314 if (r.bufferedLen() != 0) return error.StreamTooLong;
315 return;
316 },
317 error.ReadFailed => return error.ReadFailed,
318 };
319 list.items.len += remaining - new_remaining;
320 remaining = new_remaining;
321 } else {
322 // Leave `buffer` empty, appending directly to `list`.
323 var dest_w: Writer = .fixed(dest);
324 const n = r.vtable.stream(r, &dest_w, .limited(dest.len)) catch |err| switch (err) {
325 error.WriteFailed => unreachable, // Prevented by the limit.
326 error.EndOfStream => return,
327 error.ReadFailed => return error.ReadFailed,
328 };
329 list.items.len += n;
330 remaining -= n;
332 const dest = cap[0..@min(cap.len, remaining + 1)];
333 writer.buffer = list.allocatedSlice();
334 writer.end = list.items.len;
335 const n = r.vtable.stream(r, &writer, .limited(dest.len)) catch |err| switch (err) {
336 error.WriteFailed => unreachable, // Prevented by the limit.
337 error.EndOfStream => return,
338 error.ReadFailed => return error.ReadFailed,
339 };
340 list.items.len += n;
341 if (n > remaining) {
342 // Move the byte to `Reader.buffer` so it is not lost.
343 assert(n - remaining == 1);
344 assert(r.end == 0);
345 r.buffer[0] = list.items[list.items.len - 1];
346 list.items.len -= 1;
347 r.end = 1;
348 return;
331349 }
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;
332384 }
333385}
334386
......@@ -340,95 +392,64 @@ pub fn appendRemaining(
340392///
341393/// The reader's internal logical seek position moves forward in accordance
342394/// with the number of bytes returned from this function.
343pub fn readVec(r: *Reader, data: []const []u8) Error!usize {
344 return readVecLimit(r, data, .unlimited);
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);
395pub fn readVec(r: *Reader, data: [][]u8) Error!usize {
396 var seek = r.seek;
357397 for (data, 0..) |buf, i| {
358 const buffer_contents = r.buffer[r.seek..r.end];
359 const copy_len = @min(buffer_contents.len, buf.len, remaining);
360 @memcpy(buf[0..copy_len], buffer_contents[0..copy_len]);
361 r.seek += copy_len;
362 remaining -= copy_len;
363 if (remaining == 0) break;
398 const contents = r.buffer[seek..r.end];
399 const copy_len = @min(contents.len, buf.len);
400 @memcpy(buf[0..copy_len], contents[0..copy_len]);
401 seek += copy_len;
364402 if (buf.len - copy_len == 0) continue;
365403
366 // All of `buffer` has been copied to `data`. We now set up a structure
367 // that enables the `Writer.writableVector` API, while also ensuring
368 // API that directly operates on the `Writable.buffer` has its minimum
369 // buffer capacity requirements met.
370 r.seek = 0;
371 r.end = 0;
372 remaining = try readVecInner(r, data[i + 1 ..], buf[copy_len..], remaining);
373 break;
404 // All of `buffer` has been copied to `data`.
405 const n = seek - r.seek;
406 r.seek = seek;
407 data[i] = buf[copy_len..];
408 defer data[i] = buf;
409 return n + try r.vtable.readVec(r, data[i..]);
374410 }
375 return @intFromEnum(limit) - remaining;
411 const n = seek - r.seek;
412 r.seek = seek;
413 return n;
376414}
377415
378fn readVecInner(r: *Reader, middle: []const []u8, first: []u8, remaining: usize) Error!usize {
379 var wrapper: Writer.VectorWrapper = .{
380 .it = .{
381 .first = first,
382 .middle = middle,
383 .last = r.buffer,
384 },
385 .writer = .{
386 .buffer = if (first.len >= r.buffer.len) first else r.buffer,
387 .vtable = Writer.VectorWrapper.vtable,
388 },
416/// Writes to `Reader.buffer` or `data`, whichever has larger capacity.
417pub fn defaultReadVec(r: *Reader, data: []const []u8) Error!usize {
418 assert(r.seek == r.end);
419 r.seek = 0;
420 r.end = 0;
421 const first = data[0];
422 const direct = first.len >= r.buffer.len;
423 var writer: Writer = .{
424 .buffer = if (direct) first else r.buffer,
425 .end = 0,
426 .vtable = &.{ .drain = Writer.fixedDrain },
389427 };
390 // If the limit may pass beyond user buffer into Reader buffer, use
391 // unlimited, allowing the Reader buffer to fill.
392 const limit: Limit = l: {
393 var n: usize = first.len;
394 for (middle) |m| n += m.len;
395 break :l if (remaining >= n) .unlimited else .limited(remaining);
428 const limit: Limit = .limited(writer.buffer.len - writer.end);
429 const n = r.vtable.stream(r, &writer, limit) catch |err| switch (err) {
430 error.WriteFailed => unreachable,
431 else => |e| return e,
396432 };
397 var n = r.vtable.stream(r, &wrapper.writer, limit) catch |err| switch (err) {
398 error.WriteFailed => {
399 assert(!wrapper.used);
400 if (wrapper.writer.buffer.ptr == first.ptr) {
401 return remaining - wrapper.writer.end;
402 } else {
403 assert(wrapper.writer.end <= r.buffer.len);
404 r.end = wrapper.writer.end;
405 return remaining;
406 }
407 },
433 if (direct) return n;
434 r.end += n;
435 return 0;
436}
437
438/// Always writes to `Reader.buffer` and returns 0.
439pub fn indirectReadVec(r: *Reader, data: []const []u8) Error!usize {
440 _ = data;
441 assert(r.seek == r.end);
442 var writer: Writer = .{
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,
408450 else => |e| return e,
409451 };
410 if (!wrapper.used) {
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;
452 return 0;
432453}
433454
434455pub fn buffered(r: *Reader) []u8 {
......@@ -642,29 +663,24 @@ pub fn readSliceAll(r: *Reader, buffer: []u8) Error!void {
642663/// See also:
643664/// * `readSliceAll`
644665pub 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;
646676 while (true) {
647 const buffer_contents = r.buffer[r.seek..r.end];
648 const dest = buffer[i..];
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) {
677 data[0] = buffer[i..];
678 i += readVec(r, &data) catch |err| switch (err) {
661679 error.EndOfStream => return i,
662680 error.ReadFailed => return error.ReadFailed,
663681 };
664 if (new_remaining_len == 0) return buffer.len;
665 i += remaining.len - new_remaining_len;
682 if (buffer.len - i == 0) return buffer.len;
666683 }
667 return buffer.len;
668684}
669685
670686/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
......@@ -1632,19 +1648,6 @@ test readVec {
16321648 try testing.expectEqualStrings(std.ascii.letters[26..], bufs[1]);
16331649}
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
16481651test "expected error.EndOfStream" {
16491652 // Unit test inspired by https://github.com/ziglang/zig/issues/17733
16501653 var buffer: [3]u8 = undefined;
......@@ -1661,6 +1664,12 @@ fn endingStream(r: *Reader, w: *Writer, limit: Limit) StreamError!usize {
16611664 return error.EndOfStream;
16621665}
16631666
1667fn endingReadVec(r: *Reader, data: []const []u8) Error!usize {
1668 _ = r;
1669 _ = data;
1670 return error.EndOfStream;
1671}
1672
16641673fn endingDiscard(r: *Reader, limit: Limit) Error!usize {
16651674 _ = r;
16661675 _ = limit;
......@@ -1797,3 +1806,57 @@ pub fn Hashed(comptime Hasher: type) type {
17971806 }
17981807 };
17991808}
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!
342342 return big_slice[0..len];
343343}
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
436345pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void {
437346 _ = try writableSliceGreedy(w, n);
438347}
......@@ -451,13 +360,6 @@ pub fn advance(w: *Writer, n: usize) void {
451360 w.end = new_end;
452361}
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
461363/// The `data` parameter is mutable because this function needs to mutate the
462364/// fields in order to handle partial writes from `VTable.writeSplat`.
463365pub 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 {
8888 .vtable = &.{
8989 .stream = stream,
9090 .rebase = rebase,
91 .discard = discard,
92 .readVec = Reader.indirectReadVec,
9193 },
9294 .buffer = buffer,
9395 .seek = 0,
......@@ -100,11 +102,23 @@ fn rebase(r: *Reader, capacity: usize) Reader.RebaseError!void {
100102 const d: *Decompress = @alignCast(@fieldParentPtr("reader", r));
101103 assert(capacity <= r.buffer.len - d.window_len);
102104 assert(r.end + capacity > r.buffer.len);
103 const discard = r.end - d.window_len;
104 const keep = r.buffer[discard..r.end];
105 const discard_n = r.end - d.window_len;
106 const keep = r.buffer[discard_n..r.end];
105107 @memmove(r.buffer[0..keep.len], keep);
106108 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;
108122}
109123
110124fn 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 {
11291129/// * Whether reading should be done via fd-to-fd syscalls (e.g. `sendfile`)
11301130/// versus plain variants (e.g. `read`).
11311131///
1132/// Fulfills the `std.io.Reader` interface.
1132/// Fulfills the `std.Io.Reader` interface.
11331133pub const Reader = struct {
11341134 file: File,
11351135 err: ?ReadError = null,
......@@ -1140,7 +1140,7 @@ pub const Reader = struct {
11401140 size: ?u64 = null,
11411141 size_err: ?GetEndPosError = null,
11421142 seek_err: ?Reader.SeekError = null,
1143 interface: std.io.Reader,
1143 interface: std.Io.Reader,
11441144
11451145 pub const SeekError = File.SeekError || error{
11461146 /// Seeking fell back to reading, and reached the end before the requested seek position.
......@@ -1177,11 +1177,12 @@ pub const Reader = struct {
11771177 }
11781178 };
11791179
1180 pub fn initInterface(buffer: []u8) std.io.Reader {
1180 pub fn initInterface(buffer: []u8) std.Io.Reader {
11811181 return .{
11821182 .vtable = &.{
11831183 .stream = Reader.stream,
11841184 .discard = Reader.discard,
1185 .readVec = Reader.readVec,
11851186 },
11861187 .buffer = buffer,
11871188 .seek = 0,
......@@ -1294,7 +1295,7 @@ pub const Reader = struct {
12941295 /// vectors through the underlying read calls as possible.
12951296 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 {
12981299 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
12991300 switch (r.mode) {
13001301 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
......@@ -1305,16 +1306,33 @@ pub const Reader = struct {
13051306 else => |e| return e,
13061307 },
13071308 .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 => {
13081328 if (is_windows) {
13091329 // Unfortunately, `ReadFileScatter` cannot be used since it
13101330 // requires page alignment.
1311 const dest = limit.slice(try w.writableSliceGreedy(1));
1312 const n = try readPositional(r, dest);
1313 w.advance(n);
1314 return n;
1331 return readPositional(r, data[0]);
13151332 }
13161333 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];
13181336 assert(dest[0].len > 0);
13191337 const n = posix.preadv(r.file.handle, dest, r.pos) catch |err| switch (err) {
13201338 error.Unseekable => {
......@@ -1339,19 +1357,22 @@ pub const Reader = struct {
13391357 return error.EndOfStream;
13401358 }
13411359 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;
13431366 },
1344 .streaming_reading => {
1367 .streaming, .streaming_reading => {
13451368 if (is_windows) {
13461369 // Unfortunately, `ReadFileScatter` cannot be used since it
13471370 // requires page alignment.
1348 const dest = limit.slice(try w.writableSliceGreedy(1));
1349 const n = try readStreaming(r, dest);
1350 w.advance(n);
1351 return n;
1371 return readStreaming(r, data[0]);
13521372 }
13531373 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];
13551376 assert(dest[0].len > 0);
13561377 const n = posix.readv(r.file.handle, dest) catch |err| {
13571378 r.err = err;
......@@ -1362,13 +1383,18 @@ pub const Reader = struct {
13621383 return error.EndOfStream;
13631384 }
13641385 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;
13661392 },
13671393 .failure => return error.ReadFailed,
13681394 }
13691395 }
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 {
13721398 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
13731399 const file = r.file;
13741400 const pos = r.pos;
......@@ -1447,7 +1473,7 @@ pub const Reader = struct {
14471473 }
14481474 }
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 {
14511477 const n = r.file.pread(dest, r.pos) catch |err| switch (err) {
14521478 error.Unseekable => {
14531479 r.mode = r.mode.toStreaming();
......@@ -1474,7 +1500,7 @@ pub const Reader = struct {
14741500 return n;
14751501 }
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 {
14781504 const n = r.file.read(dest) catch |err| {
14791505 r.err = err;
14801506 return error.ReadFailed;
......@@ -1487,7 +1513,7 @@ pub const Reader = struct {
14871513 return n;
14881514 }
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 {
14911517 switch (r.mode) {
14921518 .positional, .positional_reading => return readPositional(r, dest),
14931519 .streaming, .streaming_reading => return readStreaming(r, dest),
......@@ -1513,7 +1539,7 @@ pub const Writer = struct {
15131539 copy_file_range_err: ?CopyFileRangeError = null,
15141540 fcopyfile_err: ?FcopyfileError = null,
15151541 seek_err: ?SeekError = null,
1516 interface: std.io.Writer,
1542 interface: std.Io.Writer,
15171543
15181544 pub const Mode = Reader.Mode;
15191545
......@@ -1550,13 +1576,13 @@ pub const Writer = struct {
15501576 };
15511577 }
15521578
1553 pub fn initInterface(buffer: []u8) std.io.Writer {
1579 pub fn initInterface(buffer: []u8) std.Io.Writer {
15541580 return .{
15551581 .vtable = &.{
15561582 .drain = drain,
15571583 .sendFile = switch (builtin.zig_backend) {
15581584 else => sendFile,
1559 .stage2_aarch64 => std.io.Writer.unimplementedSendFile,
1585 .stage2_aarch64 => std.Io.Writer.unimplementedSendFile,
15601586 },
15611587 },
15621588 .buffer = buffer,
......@@ -1574,7 +1600,7 @@ pub const Writer = struct {
15741600 };
15751601 }
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 {
15781604 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
15791605 const handle = w.file.handle;
15801606 const buffered = io_w.buffered();
......@@ -1724,10 +1750,10 @@ pub const Writer = struct {
17241750 }
17251751
17261752 pub fn sendFile(
1727 io_w: *std.io.Writer,
1753 io_w: *std.Io.Writer,
17281754 file_reader: *Reader,
1729 limit: std.io.Limit,
1730 ) std.io.Writer.FileError!usize {
1755 limit: std.Io.Limit,
1756 ) std.Io.Writer.FileError!usize {
17311757 const reader_buffered = file_reader.interface.buffered();
17321758 if (reader_buffered.len >= @intFromEnum(limit))
17331759 return sendFileBuffered(io_w, file_reader, reader_buffered);
......@@ -1989,10 +2015,10 @@ pub const Writer = struct {
19892015 }
19902016
19912017 fn sendFileBuffered(
1992 io_w: *std.io.Writer,
2018 io_w: *std.Io.Writer,
19932019 file_reader: *Reader,
19942020 reader_buffered: []const u8,
1995 ) std.io.Writer.FileError!usize {
2021 ) std.Io.Writer.FileError!usize {
19962022 const n = try drain(io_w, &.{reader_buffered}, 1);
19972023 file_reader.seekTo(file_reader.pos + n) catch return error.ReadFailed;
19982024 return n;
......@@ -2015,7 +2041,7 @@ pub const Writer = struct {
20152041 }
20162042 }
20172043
2018 pub const EndError = SetEndPosError || std.io.Writer.Error;
2044 pub const EndError = SetEndPosError || std.Io.Writer.Error;
20192045
20202046 /// Flushes any buffered data and sets the end position of the file.
20212047 ///
lib/std/net.zig+34-18
......@@ -7,7 +7,7 @@ const net = @This();
77const mem = std.mem;
88const posix = std.posix;
99const fs = std.fs;
10const io = std.io;
10const Io = std.Io;
1111const native_endian = builtin.target.cpu.arch.endian();
1212const native_os = builtin.os.tag;
1313const windows = std.os.windows;
......@@ -165,7 +165,7 @@ pub const Address = extern union {
165165 }
166166 }
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 {
169169 switch (self.any.family) {
170170 posix.AF.INET => try self.in.format(w),
171171 posix.AF.INET6 => try self.in6.format(w),
......@@ -342,7 +342,7 @@ pub const Ip4Address = extern struct {
342342 self.sa.port = mem.nativeToBig(u16, port);
343343 }
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 {
346346 const bytes: *const [4]u8 = @ptrCast(&self.sa.addr);
347347 try w.print("{d}.{d}.{d}.{d}:{d}", .{ bytes[0], bytes[1], bytes[2], bytes[3], self.getPort() });
348348 }
......@@ -633,7 +633,7 @@ pub const Ip6Address = extern struct {
633633 self.sa.port = mem.nativeToBig(u16, port);
634634 }
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 {
637637 const port = mem.bigToNative(u16, self.sa.port);
638638 if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
639639 try w.print("[::ffff:{d}.{d}.{d}.{d}]:{d}", .{
......@@ -1348,7 +1348,7 @@ fn parseHosts(
13481348 name: []const u8,
13491349 family: posix.sa_family_t,
13501350 port: u16,
1351 br: *io.Reader,
1351 br: *Io.Reader,
13521352) error{ OutOfMemory, ReadFailed }!void {
13531353 while (true) {
13541354 const line = br.takeDelimiterExclusive('\n') catch |err| switch (err) {
......@@ -1402,7 +1402,7 @@ test parseHosts {
14021402 // TODO parsing addresses should not have OS dependencies
14031403 return error.SkipZigTest;
14041404 }
1405 var reader: std.io.Reader = .fixed(
1405 var reader: Io.Reader = .fixed(
14061406 \\127.0.0.1 localhost
14071407 \\::1 localhost
14081408 \\127.0.0.2 abcd
......@@ -1583,7 +1583,7 @@ const ResolvConf = struct {
15831583 const Directive = enum { options, nameserver, domain, search };
15841584 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 {
15871587 const gpa = rc.gpa;
15881588 while (reader.takeSentinel('\n')) |line_with_comment| {
15891589 const line = line: {
......@@ -1894,7 +1894,7 @@ pub const Stream = struct {
18941894 pub const Reader = switch (native_os) {
18951895 .windows => struct {
18961896 /// Use `interface` for portable code.
1897 interface_state: io.Reader,
1897 interface_state: Io.Reader,
18981898 /// Use `getStream` for portable code.
18991899 net_stream: Stream,
19001900 /// Use `getError` for portable code.
......@@ -1910,14 +1910,17 @@ pub const Stream = struct {
19101910 return r.error_state;
19111911 }
19121912
1913 pub fn interface(r: *Reader) *io.Reader {
1913 pub fn interface(r: *Reader) *Io.Reader {
19141914 return &r.interface_state;
19151915 }
19161916
19171917 pub fn init(net_stream: Stream, buffer: []u8) Reader {
19181918 return .{
19191919 .interface_state = .{
1920 .vtable = &.{ .stream = stream },
1920 .vtable = &.{
1921 .stream = stream,
1922 .readVec = readVec,
1923 },
19211924 .buffer = buffer,
19221925 .seek = 0,
19231926 .end = 0,
......@@ -1927,16 +1930,29 @@ pub const Stream = struct {
19271930 };
19281931 }
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 {
19311941 const r: *Reader = @alignCast(@fieldParentPtr("interface_state", io_r));
19321942 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];
19341945 assert(bufs[0].len != 0);
19351946 const n = streamBufs(r, bufs) catch |err| {
19361947 r.error_state = err;
19371948 return error.ReadFailed;
19381949 };
19391950 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 }
19401956 return n;
19411957 }
19421958
......@@ -1968,7 +1984,7 @@ pub const Stream = struct {
19681984
19691985 pub const Error = ReadError;
19701986
1971 pub fn interface(r: *Reader) *io.Reader {
1987 pub fn interface(r: *Reader) *Io.Reader {
19721988 return &r.file_reader.interface;
19731989 }
19741990
......@@ -1996,7 +2012,7 @@ pub const Stream = struct {
19962012 pub const Writer = switch (native_os) {
19972013 .windows => struct {
19982014 /// This field is present on all systems.
1999 interface: io.Writer,
2015 interface: Io.Writer,
20002016 /// Use `getStream` for cross-platform support.
20012017 stream: Stream,
20022018 /// This field is present on all systems.
......@@ -2034,7 +2050,7 @@ pub const Stream = struct {
20342050 }
20352051 }
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 {
20382054 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
20392055 const buffered = io_w.buffered();
20402056 comptime assert(native_os == .windows);
......@@ -2106,7 +2122,7 @@ pub const Stream = struct {
21062122 },
21072123 else => struct {
21082124 /// This field is present on all systems.
2109 interface: io.Writer,
2125 interface: Io.Writer,
21102126
21112127 err: ?Error = null,
21122128 file_writer: File.Writer,
......@@ -2138,7 +2154,7 @@ pub const Stream = struct {
21382154 i.* += 1;
21392155 }
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 {
21422158 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
21432159 const buffered = io_w.buffered();
21442160 var iovecs: [max_buffers_len]posix.iovec_const = undefined;
......@@ -2190,7 +2206,7 @@ pub const Stream = struct {
21902206 });
21912207 }
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 {
21942210 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
21952211 const n = try w.file_writer.interface.sendFileHeader(io_w.buffered(), file_reader, limit);
21962212 return io_w.consume(n);