From 2703e94cdd934a3575f803641f3f59ad7012b37e Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Fri, 2 Jan 2026 19:10:01 -0800 Subject: [PATCH] add Readable.readAllAlloc() --- nio.zig | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/nio.zig b/nio.zig index 7313338cff808f56e7b62980541f792005cbc354..6c074d2959625a3cda1aaeb285ad90dbd159e900 100644 --- a/nio.zig +++ b/nio.zig @@ -44,6 +44,48 @@ pub fn Readable(T: type, this_kind: enum { _var, _const, _bare }) type { const amt_read = try self.readAll(buf); if (amt_read < buf.len) return error.EndOfStream; } + + /// Appends to the `std.ArrayList` contents by reading from the stream until end of stream is found. + /// If the number of bytes appended would exceed `max_append_size`, `error.StreamTooLong` is returned and the `std.ArrayList` has exactly `max_append_size` bytes appended. + fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void { + return self.readAllArrayListAligned(null, array_list, max_append_size); + } + + fn readAllArrayListAligned(self: Self, comptime alignment: ?u29, array_list: *std.ArrayListAligned(u8, alignment), max_append_size: usize) !void { + try array_list.ensureTotalCapacity(@min(max_append_size, 4096)); + const original_len = array_list.items.len; + var start_index: usize = original_len; + while (true) { + array_list.expandToCapacity(); + const dest_slice = array_list.items[start_index..]; + const bytes_read = try self.readAll(dest_slice); + start_index += bytes_read; + + if (start_index - original_len > max_append_size) { + array_list.shrinkAndFree(original_len + max_append_size); + return error.StreamTooLong; + } + + if (bytes_read != dest_slice.len) { + array_list.shrinkAndFree(start_index); + return; + } + + // This will trigger ArrayList to expand superlinearly at whatever its growth rate is. + try array_list.ensureTotalCapacity(start_index + 1); + } + } + + /// Allocates enough memory to hold all the contents of the stream. + /// If the allocated memory would be greater than `max_size`, returns `error.StreamTooLong`. + /// Caller owns returned memory. + /// If this function returns an error, the contents from the stream read so far are lost. + pub fn readAllAlloc(self: Self, allocator: std.mem.Allocator, max_size: usize) ![]u8 { + var array_list = std.ArrayList(u8).init(allocator); + defer array_list.deinit(); + try self.readAllArrayList(&array_list, max_size); + return try array_list.toOwnedSlice(); + } }; } -- 2.54.0