| author | |
| committer | |
| log | cf7a28febbbe877003d8d4f9a13ceb94698c1e3e |
| tree | 73f0874da561173692d69cccd25cc83de308f809 |
| parent | a9773944dc2b930facd66350dcbe87178aacf487 |
simplifies and fixes things
addresses a subset of #246085 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 may | 44 | /// 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 of | 46 | /// 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, |
| 49 | 49 | ||
| 50 | /// Consumes bytes from the internally tracked stream position without | 50 | /// 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, |
| 70 | 70 | ||
| 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 ends | 88 | /// 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 instead | 159 | // 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 | } |
| 179 | 194 | ||
| 180 | pub fn defaultDiscard(r: *Reader, limit: Limit) Error!usize { | 195 | pub 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 | |||
| 354 | pub const UnlimitedAllocError = Allocator.Error || ShortError; | ||
| 355 | |||
| 356 | pub 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 | } |
| 334 | 386 | ||
| ... | @@ -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 accordance | 393 | /// 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. |
| 343 | pub fn readVec(r: *Reader, data: []const []u8) Error!usize { | 395 | pub 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. | ||
| 354 | pub 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; |
| 365 | 403 | ||
| 366 | // All of `buffer` has been copied to `data`. We now set up a structure | 404 | // All of `buffer` has been copied to `data`. |
| 367 | // that enables the `Writer.writableVector` API, while also ensuring | 405 | const n = seek - r.seek; |
| 368 | // API that directly operates on the `Writable.buffer` has its minimum | 406 | 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 | } |
| 377 | 415 | ||
| 378 | fn 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 = .{ | 417 | pub 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, use | 428 | 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); | 439 | pub 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 | } |
| 433 | 454 | ||
| 434 | pub fn buffered(r: *Reader) []u8 { | 455 | pub 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` |
| 644 | pub fn readSliceShort(r: *Reader, buffer: []u8) ShortError!usize { | 665 | pub 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 | } |
| 669 | 685 | ||
| 670 | /// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing | 686 | /// 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 | } |
| 1634 | 1650 | ||
| 1635 | test 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 | |||
| 1648 | test "expected error.EndOfStream" { | 1651 | test "expected error.EndOfStream" { |
| 1649 | // Unit test inspired by https://github.com/ziglang/zig/issues/17733 | 1652 | // 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 | } |
| 1663 | 1666 | ||
| 1667 | fn endingReadVec(r: *Reader, data: []const []u8) Error!usize { | ||
| 1668 | _ = r; | ||
| 1669 | _ = data; | ||
| 1670 | return error.EndOfStream; | ||
| 1671 | } | ||
| 1672 | |||
| 1664 | fn endingDiscard(r: *Reader, limit: Limit) Error!usize { | 1673 | fn 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 | |||
| 1810 | pub 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 | |||
| 1830 | pub 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 | } |
| 344 | 344 | ||
| 345 | pub 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 | |||
| 374 | pub 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 | |||
| 385 | pub 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 | |||
| 394 | pub 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 | |||
| 410 | pub 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 | |||
| 436 | pub fn ensureUnusedCapacity(w: *Writer, n: usize) Error!void { | 345 | pub 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 | } |
| 453 | 362 | ||
| 454 | /// After calling `writableVector`, this function tracks how many bytes were | ||
| 455 | /// written to it. | ||
| 456 | pub 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 the | 363 | /// 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`. |
| 463 | pub fn writeVecAll(w: *Writer, data: [][]const u8) Error!void { | 365 | pub 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 | |||
| 112 | fn 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 | } |
| 109 | 123 | ||
| 110 | fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize { | 124 | fn 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. |
| 1133 | pub const Reader = struct { | 1133 | pub 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, |
| 1144 | 1144 | ||
| 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 | }; |
| 1179 | 1179 | ||
| 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; |
| 1296 | 1297 | ||
| 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 it | 1329 | // 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 it | 1369 | // 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 | } |
| 1370 | 1396 | ||
| 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 | } |
| 1449 | 1475 | ||
| 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 | } |
| 1476 | 1502 | ||
| 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 | } |
| 1489 | 1515 | ||
| 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, |
| 1517 | 1543 | ||
| 1518 | pub const Mode = Reader.Mode; | 1544 | pub const Mode = Reader.Mode; |
| 1519 | 1545 | ||
| ... | @@ -1550,13 +1576,13 @@ pub const Writer = struct { | ... | @@ -1550,13 +1576,13 @@ pub const Writer = struct { |
| 1550 | }; | 1576 | }; |
| 1551 | } | 1577 | } |
| 1552 | 1578 | ||
| 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 | } |
| 1576 | 1602 | ||
| 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 | } |
| 1725 | 1751 | ||
| 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 | } |
| 1990 | 2016 | ||
| 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 | } |
| 2017 | 2043 | ||
| 2018 | pub const EndError = SetEndPosError || std.io.Writer.Error; | 2044 | pub const EndError = SetEndPosError || std.Io.Writer.Error; |
| 2019 | 2045 | ||
| 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(); |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | const posix = std.posix; | 8 | const posix = std.posix; |
| 9 | const fs = std.fs; | 9 | const fs = std.fs; |
| 10 | const io = std.io; | 10 | const Io = std.Io; |
| 11 | const native_endian = builtin.target.cpu.arch.endian(); | 11 | const native_endian = builtin.target.cpu.arch.endian(); |
| 12 | const native_os = builtin.os.tag; | 12 | const native_os = builtin.os.tag; |
| 13 | const windows = std.os.windows; | 13 | const 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 | } |
| 167 | 167 | ||
| 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 | } |
| 344 | 344 | ||
| 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 | } |
| 635 | 635 | ||
| 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 dependencies | 1402 | // 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 localhost | 1406 | \\127.0.0.1 localhost |
| 1407 | \\::1 localhost | 1407 | \\::1 localhost |
| 1408 | \\127.0.0.2 abcd | 1408 | \\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 }; |
| 1585 | 1585 | ||
| 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 | } |
| 1912 | 1912 | ||
| 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 | } |
| 1916 | 1916 | ||
| 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 | } |
| 1929 | 1932 | ||
| 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 | } |
| 1942 | 1958 | ||
| ... | @@ -1968,7 +1984,7 @@ pub const Stream = struct { | ... | @@ -1968,7 +1984,7 @@ pub const Stream = struct { |
| 1968 | 1984 | ||
| 1969 | pub const Error = ReadError; | 1985 | pub const Error = ReadError; |
| 1970 | 1986 | ||
| 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 | } |
| 1974 | 1990 | ||
| ... | @@ -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 | } |
| 2036 | 2052 | ||
| 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, |
| 2110 | 2126 | ||
| 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 | } |
| 2140 | 2156 | ||
| 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 | } |
| 2192 | 2208 | ||
| 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); |