authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-09-15 15:18:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-09-16 07:31:00+02:00
log2ec5523d5a70db80bac8563e1f06d3b164f53db3
treeb5cc7204b7190c8f968335b94843c30e9cb1862e
parent5c4b14b6828b6de047a00e342de354cb69367f72

Reader.allocRemaining: only error with StreamTooLong when limit is exceeded

Follow up to #36708 and #36692. The intended behavior of this function was what was previously documented: that StreamTooLong was only returned when the limit was *exceeded*. However, that was never implemented. This commit implements the intended behavior, with the added caveat that `limit + 1` bytes will be discarded if the limit is exceeded.

1 files changed, 33 insertions(+), 4 deletions(-)

lib/std/Io/Reader.zig+33-4
...@@ -287,16 +287,20 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo...@@ -287,16 +287,20 @@ pub const LimitedAllocError = Allocator.Error || ShortError || error{StreamTooLo
287/// Transfers all bytes from the current position to the end of the stream, up287/// Transfers all bytes from the current position to the end of the stream, up
288/// to `limit`, returning them as a caller-owned allocated slice.288/// to `limit`, returning them as a caller-owned allocated slice.
289///289///
290/// If `limit` is reached or exceeded, `error.StreamTooLong` is returned290/// If `limit` is exceeded, `error.StreamTooLong` is returned instead.
291/// instead. In such case, the next byte that would be read will be the first291/// In such case, the next byte that would be read will be one byte past the
292/// one to exceed `limit`, and all preceeding bytes have been discarded.292/// first one to exceed `limit`, and all preceeding bytes have been discarded.
293///293///
294/// See also:294/// See also:
295/// * `appendRemaining`295/// * `appendRemaining`
296pub fn allocRemaining(r: *Reader, gpa: Allocator, limit: Limit) LimitedAllocError![]u8 {296pub fn allocRemaining(r: *Reader, gpa: Allocator, limit: Limit) LimitedAllocError![]u8 {
297 var buffer: ArrayList(u8) = .empty;297 var buffer: ArrayList(u8) = .empty;
298 defer buffer.deinit(gpa);298 defer buffer.deinit(gpa);
299 try appendRemaining(r, gpa, &buffer, limit);299
300 try appendRemaining(r, gpa, &buffer, switch (limit) {
301 .unlimited => limit,
302 .nothing, _ => .limited(@backingInt(limit) + 1),
303 });
300 return buffer.toOwnedSlice(gpa);304 return buffer.toOwnedSlice(gpa);
301}305}
302306
...@@ -1894,6 +1898,31 @@ test "takeStruct and peekStruct packed" {...@@ -1894,6 +1898,31 @@ test "takeStruct and peekStruct packed" {
1894 try testing.expectError(error.EndOfStream, r.takeStruct(S, .little));1898 try testing.expectError(error.EndOfStream, r.takeStruct(S, .little));
1895}1899}
18961900
1901test allocRemaining {
1902 {
1903 var r: Reader = .fixed("abc");
1904 const str = try r.allocRemaining(testing.allocator, .unlimited);
1905 defer testing.allocator.free(str);
1906 try testing.expectEqualStrings("abc", str);
1907 }
1908 {
1909 var r: Reader = .fixed("abc");
1910 const str = try r.allocRemaining(testing.allocator, .limited(5));
1911 defer testing.allocator.free(str);
1912 try testing.expectEqualStrings("abc", str);
1913 }
1914 {
1915 var r: Reader = .fixed("abc");
1916 const str = try r.allocRemaining(testing.allocator, .limited(3));
1917 defer testing.allocator.free(str);
1918 try testing.expectEqualStrings("abc", str);
1919 }
1920 {
1921 var r: Reader = .fixed("abcd");
1922 try testing.expectError(error.StreamTooLong, r.allocRemaining(testing.allocator, .limited(3)));
1923 }
1924}
1925
1897/// Provides a `Reader` implementation by passing data from an underlying1926/// Provides a `Reader` implementation by passing data from an underlying
1898/// reader through `Hasher.update`.1927/// reader through `Hasher.update`.
1899///1928///