master
  1const Limited = @This();
  2
  3const std = @import("../../std.zig");
  4const Reader = std.Io.Reader;
  5const Writer = std.Io.Writer;
  6const Limit = std.Io.Limit;
  7
  8unlimited: *Reader,
  9remaining: Limit,
 10interface: Reader,
 11
 12pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited {
 13    return .{
 14        .unlimited = reader,
 15        .remaining = limit,
 16        .interface = .{
 17            .vtable = &.{
 18                .stream = stream,
 19                .discard = discard,
 20            },
 21            .buffer = buffer,
 22            .seek = 0,
 23            .end = 0,
 24        },
 25    };
 26}
 27
 28fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
 29    const l: *Limited = @fieldParentPtr("interface", r);
 30    if (l.remaining == .nothing) return error.EndOfStream;
 31    const combined_limit = limit.min(l.remaining);
 32    const n = try l.unlimited.stream(w, combined_limit);
 33    l.remaining = l.remaining.subtract(n).?;
 34    return n;
 35}
 36
 37test stream {
 38    var orig_buf: [10]u8 = undefined;
 39    @memcpy(&orig_buf, "test bytes");
 40    var fixed: std.Io.Reader = .fixed(&orig_buf);
 41
 42    var limit_buf: [1]u8 = undefined;
 43    var limited: std.Io.Reader.Limited = .init(&fixed, @enumFromInt(4), &limit_buf);
 44
 45    var result_buf: [10]u8 = undefined;
 46    var fixed_writer: std.Io.Writer = .fixed(&result_buf);
 47    const streamed = try limited.interface.stream(&fixed_writer, @enumFromInt(7));
 48
 49    try std.testing.expect(streamed == 4);
 50    try std.testing.expectEqualStrings("test", result_buf[0..streamed]);
 51}
 52
 53fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
 54    const l: *Limited = @fieldParentPtr("interface", r);
 55    if (l.remaining == .nothing) return error.EndOfStream;
 56    const combined_limit = limit.min(l.remaining);
 57    const n = try l.unlimited.discard(combined_limit);
 58    l.remaining = l.remaining.subtract(n).?;
 59    return n;
 60}
 61
 62test "end of stream, read, hit limit exactly" {
 63    var f: Reader = .fixed("i'm dying");
 64    var l = f.limited(.limited(4), &.{});
 65    const r = &l.interface;
 66
 67    var buf: [2]u8 = undefined;
 68    try r.readSliceAll(&buf);
 69    try r.readSliceAll(&buf);
 70    try std.testing.expectError(error.EndOfStream, l.interface.readSliceAll(&buf));
 71}
 72
 73test "end of stream, read, hit limit after partial read" {
 74    var f: Reader = .fixed("i'm dying");
 75    var l = f.limited(.limited(5), &.{});
 76    const r = &l.interface;
 77
 78    var buf: [2]u8 = undefined;
 79    try r.readSliceAll(&buf);
 80    try r.readSliceAll(&buf);
 81    try std.testing.expectError(error.EndOfStream, l.interface.readSliceAll(&buf));
 82}
 83
 84test "end of stream, discard, hit limit exactly" {
 85    var f: Reader = .fixed("i'm dying");
 86    var l = f.limited(.limited(4), &.{});
 87    const r = &l.interface;
 88
 89    try r.discardAll(2);
 90    try r.discardAll(2);
 91    try std.testing.expectError(error.EndOfStream, l.interface.discardAll(2));
 92}
 93
 94test "end of stream, discard, hit limit after partial read" {
 95    var f: Reader = .fixed("i'm dying");
 96    var l = f.limited(.limited(5), &.{});
 97    const r = &l.interface;
 98
 99    try r.discardAll(2);
100    try r.discardAll(2);
101    try std.testing.expectError(error.EndOfStream, l.interface.discardAll(2));
102}