| ... | @@ -106,7 +106,7 @@ const ending_state: Reader = .fixed(&.{}); | ... | @@ -106,7 +106,7 @@ const ending_state: Reader = .fixed(&.{}); |
| 106 | pub const ending: *Reader = @constCast(&ending_state); | 106 | pub const ending: *Reader = @constCast(&ending_state); |
| 107 | | 107 | |
| 108 | pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited { | 108 | pub fn limited(r: *Reader, limit: Limit, buffer: []u8) Limited { |
| 109 | return Limited.init(r, limit, buffer); | 109 | return .init(r, limit, buffer); |
| 110 | } | 110 | } |
| 111 | | 111 | |
| 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 |
| 925 | pub 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` |
| | 930 | pub 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 | } |
| 930 | | 940 | |
| 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. |
| 935 | pub 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` |
| | 952 | pub 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 | |
| | 959 | pub 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. |
| | 972 | pub 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 | } |
| 940 | | 989 | |
| 941 | /// Fills the buffer such that it contains at least `n` bytes, without | 990 | /// 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 | } |
| 1378 | | 1440 | |
| 1379 | test discardDelimiterExclusive { | 1441 | test 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 | } |
| 1382 | | 1448 | |
| 1383 | test discardDelimiterInclusive { | 1449 | test 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 | |
| | 1455 | test 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 | } |
| 1386 | | 1463 | |
| 1387 | test fill { | 1464 | test fill { |
| 1388 | return error.Unimplemented; | 1465 | var r: Reader = .fixed("abc"); |
| | 1466 | try r.fill(1); |
| | 1467 | try r.fill(3); |
| 1389 | } | 1468 | } |
| 1390 | | 1469 | |
| 1391 | test takeByte { | 1470 | test 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 | } |
| 1394 | | 1476 | |
| 1395 | test takeByteSigned { | 1477 | test 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 | } |
| 1398 | | 1483 | |
| 1399 | test takeInt { | 1484 | test 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 | } |
| 1402 | | 1489 | |
| 1403 | test takeVarInt { | 1490 | test 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 | } |
| 1406 | | 1496 | |
| 1407 | test takeStruct { | 1497 | test takeStruct { |