authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 19:14:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-09 12:08:36-07:00
log6a25bfc4751ab959f32390963d902bec731129d8
tree8c571a4bb06990b52699e208fd55d3b62ae7a1ad
parent52730f3f2191aa242b030b9c5f3ca33a7f4fea88

std.Io.Reader: rework peekDelimiterInclusive

Now it's based on calling fillMore rather than an illegal aliased stream into the Reader buffer. This commit also includes a disambiguation block inspired by #25162. If `StreamTooLong` was added to `RebaseError` then this logic could be replaced by removing the exit condition from the while loop. That error code would represent when `buffer` capacity is too small for an operation, replacing the current use of asserts.

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

lib/std/Io/Reader.zig+28-19
...@@ -760,28 +760,37 @@ pub fn takeDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {...@@ -760,28 +760,37 @@ pub fn takeDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
760/// * `peekDelimiterExclusive`760/// * `peekDelimiterExclusive`
761/// * `takeDelimiterInclusive`761/// * `takeDelimiterInclusive`
762pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {762pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
763 const buffer = r.buffer[0..r.end];763 {
764 const seek = r.seek;764 const contents = r.buffer[0..r.end];
765 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |delimiter_index| {765 const seek = r.seek;
766 @branchHint(.likely);766 if (std.mem.findScalarPos(u8, contents, seek, delimiter)) |end| {
767 return buffer[seek .. delimiter_index + 1];767 @branchHint(.likely);
768 return contents[seek .. end + 1];
769 }
768 }770 }
769 // TODO take a parameter for max search length rather than relying on buffer capacity771 while (true) {
770 try rebase(r, r.buffer.len);772 const content_len = r.end - r.seek;
771 while (r.buffer.len - r.end != 0) {773 if (r.buffer.len - content_len == 0) break;
772 const existing_buffered_len = r.end - r.seek;774 try fillMore(r);
773 const end_cap = r.buffer[r.end..];775 const seek = r.seek;
774 var writer: Writer = .fixed(end_cap);776 const contents = r.buffer[0..r.end];
775 const n = r.vtable.stream(r, &writer, .limited(end_cap.len)) catch |err| switch (err) {777 if (std.mem.findScalarPos(u8, contents, seek + content_len, delimiter)) |end| {
776 error.WriteFailed => unreachable,778 return contents[seek .. end + 1];
777 else => |e| return e,
778 };
779 r.end += n;
780 if (std.mem.indexOfScalarPos(u8, r.buffer[0..r.end], r.seek + existing_buffered_len, delimiter)) |delimiter_index| {
781 return r.buffer[r.seek .. delimiter_index + 1];
782 }779 }
783 }780 }
784 return error.StreamTooLong;781 // It might or might not be end of stream. There is no more buffer space
782 // left to disambiguate. If `StreamTooLong` was added to `RebaseError` then
783 // this logic could be replaced by removing the exit condition from the
784 // above while loop. That error code would represent when `buffer` capacity
785 // is too small for an operation, replacing the current use of asserts.
786 var failing_writer = Writer.failing;
787 while (r.vtable.stream(r, &failing_writer, .limited(1))) |n| {
788 assert(n == 0);
789 } else |err| switch (err) {
790 error.WriteFailed => return error.StreamTooLong,
791 error.ReadFailed => |e| return e,
792 error.EndOfStream => |e| return e,
793 }
785}794}
786795
787/// Returns a slice of the next bytes of buffered data from the stream until796/// Returns a slice of the next bytes of buffered data from the stream until