| ... | @@ -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` |
| 762 | pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { | 762 | pub 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 capacity | 771 | 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 | } |
| 786 | | 795 | |
| 787 | /// Returns a slice of the next bytes of buffered data from the stream until | 796 | /// Returns a slice of the next bytes of buffered data from the stream until |