authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-06-30 15:37:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-07 22:43:52-07:00
logd03d2731175c5e775dccaef3ef3329f98c4e2c70
tree719c6ef7a69ebbd64fcff9cb5797dc27eb1bd3e0
parent1e1f026c8355c8cd4c6efb5b7fc42e5b2091e1f2

std.io.Reader: fix fill implementation

now it avoids writing to buffer in the case of fixed

1 files changed, 109 insertions(+), 19 deletions(-)

lib/std/io/Reader.zig+109-19
...@@ -106,7 +106,7 @@ const ending_state: Reader = .fixed(&.{});...@@ -106,7 +106,7 @@ const ending_state: Reader = .fixed(&.{});
106pub const ending: *Reader = @constCast(&ending_state);106pub const ending: *Reader = @constCast(&ending_state);
107107
108pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited {108pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited {
109 return Limited.init(r, limit, buffer);109 return .init(r, limit, buffer);
110}110}
111111
112/// Constructs a `Reader` such that it will read from `buffer` and then end.112/// Constructs a `Reader` such that it will read from `buffer` and then end.
...@@ -921,21 +921,70 @@ pub fn streamDelimiterLimit(...@@ -921,21 +921,70 @@ pub fn streamDelimiterLimit(
921/// Reads from the stream until specified byte is found, discarding all data,921/// Reads from the stream until specified byte is found, discarding all data,
922/// including the delimiter.922/// including the delimiter.
923///923///
924/// If end of stream is found, this function succeeds.924/// Returns number of bytes discarded, or `error.EndOfStream` if the delimiter
925pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!void {925/// is not found.
926 _ = r;926///
927 _ = delimiter;927/// See also:
928 @panic("TODO");928/// * `discardDelimiterExclusive`
929/// * `discardDelimiterLimit`
930pub fn discardDelimiterInclusive(r: *Reader, delimiter: u8) Error!usize {
931 const n = discardDelimiterLimit(r, delimiter, .unlimited) catch |err| switch (err) {
932 error.StreamTooLong => unreachable, // unlimited is passed
933 else => |e| return e,
934 };
935 if (r.seek == r.end) return error.EndOfStream;
936 assert(r.buffer[r.seek] == delimiter);
937 toss(r, 1);
938 return n + 1;
929}939}
930940
931/// Reads from the stream until specified byte is found, discarding all data,941/// Reads from the stream until specified byte is found, discarding all data,
932/// excluding the delimiter.942/// excluding the delimiter.
933///943///
934/// Succeeds if stream ends before delimiter found.944/// Returns the number of bytes discarded.
935pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!void {945///
936 _ = r;946/// Succeeds if stream ends before delimiter found. End of stream can be
937 _ = delimiter;947/// detected by checking if the delimiter is buffered.
938 @panic("TODO");948///
949/// See also:
950/// * `discardDelimiterInclusive`
951/// * `discardDelimiterLimit`
952pub fn discardDelimiterExclusive(r: *Reader, delimiter: u8) ShortError!usize {
953 return discardDelimiterLimit(r, delimiter, .unlimited) catch |err| switch (err) {
954 error.StreamTooLong => unreachable, // unlimited is passed
955 else => |e| return e,
956 };
957}
958
959pub const DiscardDelimiterLimitError = error{
960 ReadFailed,
961 /// The delimiter was not found within the limit.
962 StreamTooLong,
963};
964
965/// Reads from the stream until specified byte is found, discarding all data,
966/// excluding the delimiter.
967///
968/// Returns the number of bytes discarded.
969///
970/// Succeeds if stream ends before delimiter found. End of stream can be
971/// detected by checking if the delimiter is buffered.
972pub fn discardDelimiterLimit(r: *Reader, delimiter: u8, limit: Limit) DiscardDelimiterLimitError!usize {
973 var remaining = @intFromEnum(limit);
974 while (remaining != 0) {
975 const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) {
976 error.ReadFailed => return error.ReadFailed,
977 error.EndOfStream => return @intFromEnum(limit) - remaining,
978 });
979 if (std.mem.indexOfScalar(u8, available, delimiter)) |delimiter_index| {
980 r.toss(delimiter_index);
981 remaining -= delimiter_index;
982 return @intFromEnum(limit) - remaining;
983 }
984 r.toss(available.len);
985 remaining -= available.len;
986 }
987 return error.StreamTooLong;
939}988}
940989
941/// Fills the buffer such that it contains at least `n` bytes, without990/// Fills the buffer such that it contains at least `n` bytes, without
...@@ -951,6 +1000,19 @@ pub fn fill(r: *Reader, n: usize) Error!void {...@@ -951,6 +1000,19 @@ pub fn fill(r: *Reader, n: usize) Error!void {
951 @branchHint(.likely);1000 @branchHint(.likely);
952 return;1001 return;
953 }1002 }
1003 if (r.seek + n <= r.buffer.len) while (true) {
1004 const end_cap = r.buffer[r.end..];
1005 var writer: Writer = .fixed(end_cap);
1006 r.end += r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) {
1007 error.WriteFailed => unreachable,
1008 else => |e| return e,
1009 };
1010 if (r.seek + n <= r.end) return;
1011 };
1012 if (r.vtable.stream == &endingStream) {
1013 // Protect the `@constCast` of `fixed`.
1014 return error.EndOfStream;
1015 }
954 rebaseCapacity(r, n);1016 rebaseCapacity(r, n);
955 var writer: Writer = .{1017 var writer: Writer = .{
956 .buffer = r.buffer,1018 .buffer = r.buffer,
...@@ -1371,37 +1433,65 @@ test streamDelimiterLimit {...@@ -1371,37 +1433,65 @@ test streamDelimiterLimit {
1371 var w: Writer = .fixed(&out_buffer);1433 var w: Writer = .fixed(&out_buffer);
1372 try testing.expectError(error.StreamTooLong, r.streamDelimiterLimit(&w, '\n', .limited(2)));1434 try testing.expectError(error.StreamTooLong, r.streamDelimiterLimit(&w, '\n', .limited(2)));
1373 try testing.expectEqual(1, try r.streamDelimiterLimit(&w, '\n', .limited(3)));1435 try testing.expectEqual(1, try r.streamDelimiterLimit(&w, '\n', .limited(3)));
1374 r.toss(1);1436 try testing.expectEqualStrings("\n", try r.take(1));
1375 try testing.expectEqual(4, try r.streamDelimiterLimit(&w, '\n', .unlimited));1437 try testing.expectEqual(4, try r.streamDelimiterLimit(&w, '\n', .unlimited));
1376 try testing.expectEqualStrings("foobars", w.buffered());1438 try testing.expectEqualStrings("foobars", w.buffered());
1377}1439}
13781440
1379test discardDelimiterExclusive {1441test discardDelimiterExclusive {
1380 return error.Unimplemented;1442 var r: Reader = .fixed("foob\nar");
1443 try testing.expectEqual(4, try r.discardDelimiterExclusive('\n'));
1444 try testing.expectEqualStrings("\n", try r.take(1));
1445 try testing.expectEqual(2, try r.discardDelimiterExclusive('\n'));
1446 try testing.expectEqual(0, try r.discardDelimiterExclusive('\n'));
1381}1447}
13821448
1383test discardDelimiterInclusive {1449test discardDelimiterInclusive {
1384 return error.Unimplemented;1450 var r: Reader = .fixed("foob\nar");
1451 try testing.expectEqual(5, try r.discardDelimiterInclusive('\n'));
1452 try testing.expectError(error.EndOfStream, r.discardDelimiterInclusive('\n'));
1453}
1454
1455test discardDelimiterLimit {
1456 var r: Reader = .fixed("foob\nar");
1457 try testing.expectError(error.StreamTooLong, r.discardDelimiterLimit('\n', .limited(4)));
1458 try testing.expectEqual(0, try r.discardDelimiterLimit('\n', .limited(2)));
1459 try testing.expectEqualStrings("\n", try r.take(1));
1460 try testing.expectEqual(2, try r.discardDelimiterLimit('\n', .unlimited));
1461 try testing.expectEqual(0, try r.discardDelimiterLimit('\n', .unlimited));
1385}1462}
13861463
1387test fill {1464test fill {
1388 return error.Unimplemented;1465 var r: Reader = .fixed("abc");
1466 try r.fill(1);
1467 try r.fill(3);
1389}1468}
13901469
1391test takeByte {1470test takeByte {
1392 return error.Unimplemented;1471 var r: Reader = .fixed("ab");
1472 try testing.expectEqual('a', try r.takeByte());
1473 try testing.expectEqual('b', try r.takeByte());
1474 try testing.expectError(error.EndOfStream, r.takeByte());
1393}1475}
13941476
1395test takeByteSigned {1477test takeByteSigned {
1396 return error.Unimplemented;1478 var r: Reader = .fixed(&.{ 255, 5 });
1479 try testing.expectEqual(-1, try r.takeByteSigned());
1480 try testing.expectEqual(5, try r.takeByteSigned());
1481 try testing.expectError(error.EndOfStream, r.takeByteSigned());
1397}1482}
13981483
1399test takeInt {1484test takeInt {
1400 return error.Unimplemented;1485 var r: Reader = .fixed(&.{ 0x12, 0x34, 0x56 });
1486 try testing.expectEqual(0x1234, try r.takeInt(u16, .big));
1487 try testing.expectError(error.EndOfStream, r.takeInt(u16, .little));
1401}1488}
14021489
1403test takeVarInt {1490test takeVarInt {
1404 return error.Unimplemented;1491 var r: Reader = .fixed(&.{ 0x12, 0x34, 0x56 });
1492 std.debug.print("{x}", .{r.buffer});
1493 try testing.expectEqual(0x123456, try r.takeVarInt(u64, .big, 3));
1494 try testing.expectError(error.EndOfStream, r.takeVarInt(u16, .little, 1));
1405}1495}
14061496
1407test takeStruct {1497test takeStruct {