| ... | @@ -846,37 +846,44 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { | ... | @@ -846,37 +846,44 @@ pub fn peekDelimiterExclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 { |
| 846 | /// Appends to `w` contents by reading from the stream until `delimiter` is | 846 | /// Appends to `w` contents by reading from the stream until `delimiter` is |
| 847 | /// found. Does not write the delimiter itself. | 847 | /// found. Does not write the delimiter itself. |
| 848 | /// | 848 | /// |
| 849 | /// Returns number of bytes streamed. | 849 | /// Returns number of bytes streamed, which may be zero, or error.EndOfStream |
| 850 | pub fn readDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize { | 850 | /// if the delimiter was not found. |
| 851 | const amount, const to = try r.readAny(w, delimiter, .unlimited); | 851 | /// |
| 852 | return switch (to) { | 852 | /// See also: |
| 853 | .delimiter => amount, | 853 | /// * `streamDelimiterEnding` |
| 854 | .limit => unreachable, | 854 | /// * `streamDelimiterLimit` |
| 855 | .end => error.EndOfStream, | 855 | pub fn streamDelimiter(r: *Reader, w: *Writer, delimiter: u8) StreamError!usize { |
| | 856 | const n = streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) { |
| | 857 | error.StreamTooLong => unreachable, // unlimited is passed |
| | 858 | else => |e| return e, |
| 856 | }; | 859 | }; |
| | 860 | if (r.seek == r.end) return error.EndOfStream; |
| | 861 | return n; |
| 857 | } | 862 | } |
| 858 | | 863 | |
| 859 | /// Appends to `w` contents by reading from the stream until `delimiter` is found. | 864 | /// Appends to `w` contents by reading from the stream until `delimiter` is found. |
| 860 | /// Does not write the delimiter itself. | 865 | /// Does not write the delimiter itself. |
| 861 | /// | 866 | /// |
| 862 | /// Succeeds if stream ends before delimiter found. | 867 | /// Returns number of bytes streamed, which may be zero. End of stream can be |
| | 868 | /// detected by checking if the next byte in the stream is the delimiter. |
| 863 | /// | 869 | /// |
| 864 | /// Returns number of bytes streamed. The end is not signaled to the writer. | 870 | /// See also: |
| 865 | pub fn readDelimiterEnding( | 871 | /// * `streamDelimiter` |
| | 872 | /// * `streamDelimiterLimit` |
| | 873 | pub fn streamDelimiterEnding( |
| 866 | r: *Reader, | 874 | r: *Reader, |
| 867 | w: *Writer, | 875 | w: *Writer, |
| 868 | delimiter: u8, | 876 | delimiter: u8, |
| 869 | ) StreamRemainingError!usize { | 877 | ) StreamRemainingError!usize { |
| 870 | const amount, const to = try r.readAny(w, delimiter, .unlimited); | 878 | return streamDelimiterLimit(r, w, delimiter, .unlimited) catch |err| switch (err) { |
| 871 | return switch (to) { | 879 | error.StreamTooLong => unreachable, // unlimited is passed |
| 872 | .delimiter, .end => amount, | 880 | else => |e| return e, |
| 873 | .limit => unreachable, | | |
| 874 | }; | 881 | }; |
| 875 | } | 882 | } |
| 876 | | 883 | |
| 877 | pub const StreamDelimiterLimitedError = StreamRemainingError || error{ | 884 | pub const StreamDelimiterLimitError = error{ |
| 878 | /// Stream ended before the delimiter was found. | 885 | ReadFailed, |
| 879 | EndOfStream, | 886 | WriteFailed, |
| 880 | /// The delimiter was not found within the limit. | 887 | /// The delimiter was not found within the limit. |
| 881 | StreamTooLong, | 888 | StreamTooLong, |
| 882 | }; | 889 | }; |
| ... | @@ -884,45 +891,31 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{ | ... | @@ -884,45 +891,31 @@ pub const StreamDelimiterLimitedError = StreamRemainingError || error{ |
| 884 | /// Appends to `w` contents by reading from the stream until `delimiter` is found. | 891 | /// Appends to `w` contents by reading from the stream until `delimiter` is found. |
| 885 | /// Does not write the delimiter itself. | 892 | /// Does not write the delimiter itself. |
| 886 | /// | 893 | /// |
| 887 | /// Returns number of bytes streamed. | 894 | /// Returns number of bytes streamed, which may be zero. End of stream can be |
| 888 | pub fn readDelimiterLimit( | 895 | /// detected by checking if the next byte in the stream is the delimiter. |
| | 896 | pub fn streamDelimiterLimit( |
| 889 | r: *Reader, | 897 | r: *Reader, |
| 890 | w: *Writer, | 898 | w: *Writer, |
| 891 | delimiter: u8, | 899 | delimiter: u8, |
| 892 | limit: Limit, | 900 | limit: Limit, |
| 893 | ) StreamDelimiterLimitedError!usize { | 901 | ) StreamDelimiterLimitError!usize { |
| 894 | const amount, const to = try r.readAny(w, delimiter, limit); | 902 | var remaining = @intFromEnum(limit); |
| 895 | return switch (to) { | 903 | while (remaining != 0) { |
| 896 | .delimiter => amount, | 904 | const available = Limit.limited(remaining).slice(r.peekGreedy(1) catch |err| switch (err) { |
| 897 | .limit => error.StreamTooLong, | 905 | error.ReadFailed => return error.ReadFailed, |
| 898 | .end => error.EndOfStream, | 906 | error.EndOfStream => return @intFromEnum(limit) - remaining, |
| 899 | }; | | |
| 900 | } | | |
| 901 | | | |
| 902 | fn readAny( | | |
| 903 | r: *Reader, | | |
| 904 | w: *Writer, | | |
| 905 | delimiter: ?u8, | | |
| 906 | limit: Limit, | | |
| 907 | ) StreamRemainingError!struct { usize, enum { delimiter, limit, end } } { | | |
| 908 | var amount: usize = 0; | | |
| 909 | var remaining = limit; | | |
| 910 | while (remaining.nonzero()) { | | |
| 911 | const available = remaining.slice(r.peekGreedy(1) catch |err| switch (err) { | | |
| 912 | error.ReadFailed => |e| return e, | | |
| 913 | error.EndOfStream => return .{ amount, .end }, | | |
| 914 | }); | 907 | }); |
| 915 | if (delimiter) |d| if (std.mem.indexOfScalar(u8, available, d)) |delimiter_index| { | 908 | if (std.mem.indexOfScalar(u8, available, delimiter)) |delimiter_index| { |
| 916 | try w.writeAll(available[0..delimiter_index]); | 909 | try w.writeAll(available[0..delimiter_index]); |
| 917 | r.toss(delimiter_index + 1); | 910 | r.toss(delimiter_index); |
| 918 | return .{ amount + delimiter_index, .delimiter }; | 911 | remaining -= delimiter_index; |
| 919 | }; | 912 | return @intFromEnum(limit) - remaining; |
| | 913 | } |
| 920 | try w.writeAll(available); | 914 | try w.writeAll(available); |
| 921 | r.toss(available.len); | 915 | r.toss(available.len); |
| 922 | amount += available.len; | 916 | remaining -= available.len; |
| 923 | remaining = remaining.subtract(available.len).?; | | |
| 924 | } | 917 | } |
| 925 | return .{ amount, .limit }; | 918 | return error.StreamTooLong; |
| 926 | } | 919 | } |
| 927 | | 920 | |
| 928 | /// 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, |
| ... | @@ -1348,15 +1341,33 @@ test peekDelimiterExclusive { | ... | @@ -1348,15 +1341,33 @@ test peekDelimiterExclusive { |
| 1348 | try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n')); | 1341 | try testing.expectEqualStrings("c", try r.peekDelimiterExclusive('\n')); |
| 1349 | } | 1342 | } |
| 1350 | | 1343 | |
| 1351 | test readDelimiter { | 1344 | test streamDelimiter { |
| 1352 | return error.Unimplemented; | 1345 | var out_buffer: [10]u8 = undefined; |
| | 1346 | var r: Reader = .fixed("foo\nbars"); |
| | 1347 | var w: Writer = .fixed(&out_buffer); |
| | 1348 | // Short streams are possible with this function but not with fixed. |
| | 1349 | try testing.expectEqual(3, try r.streamDelimiter(&w, '\n')); |
| | 1350 | try testing.expectEqualStrings("foo", w.buffered()); |
| | 1351 | try testing.expectEqual(0, try r.streamDelimiter(&w, '\n')); |
| | 1352 | r.toss(1); |
| | 1353 | try testing.expectError(error.EndOfStream, r.streamDelimiter(&w, '\n')); |
| 1353 | } | 1354 | } |
| 1354 | | 1355 | |
| 1355 | test readDelimiterEnding { | 1356 | test streamDelimiterEnding { |
| 1356 | return error.Unimplemented; | 1357 | var out_buffer: [10]u8 = undefined; |
| | 1358 | var r: Reader = .fixed("foo\nbars"); |
| | 1359 | var w: Writer = .fixed(&out_buffer); |
| | 1360 | // Short streams are possible with this function but not with fixed. |
| | 1361 | try testing.expectEqual(3, try r.streamDelimiterEnding(&w, '\n')); |
| | 1362 | try testing.expectEqualStrings("foo", w.buffered()); |
| | 1363 | r.toss(1); |
| | 1364 | try testing.expectEqual(4, try r.streamDelimiterEnding(&w, '\n')); |
| | 1365 | try testing.expectEqualStrings("foobars", w.buffered()); |
| | 1366 | try testing.expectEqual(0, try r.streamDelimiterEnding(&w, '\n')); |
| | 1367 | try testing.expectEqual(0, try r.streamDelimiterEnding(&w, '\n')); |
| 1357 | } | 1368 | } |
| 1358 | | 1369 | |
| 1359 | test readDelimiterLimit { | 1370 | test streamDelimiterLimit { |
| 1360 | return error.Unimplemented; | 1371 | return error.Unimplemented; |
| 1361 | } | 1372 | } |
| 1362 | | 1373 | |