authorgravatar for bratishkaerik@getgoogleoff.meEric Joldasov <bratishkaerik@getgoogleoff.me> 2023-05-19 01:42:33+06:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-03 13:38:23-07:00
log5c6f111379d81cf017c8c6eaa4c7c76632acf4d6
tree30a33830e4e56c86f12b3239cb92eee4bd0150fd
parent1a4b0d979027069d55ef354e6d04ea10cf455344

std.io.reader.Reader: add `streamUntilDelimiter`

Signed-off-by: Eric Joldasov <bratishkaerik@getgoogleoff.me>

1 files changed, 76 insertions(+), 58 deletions(-)

lib/std/io/reader.zig+76-58
...@@ -51,7 +51,7 @@ pub fn Reader(...@@ -51,7 +51,7 @@ pub fn Reader(
51 }51 }
5252
53 /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.53 /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
54 pub fn readNoEof(self: Self, buf: []u8) !void {54 pub fn readNoEof(self: Self, buf: []u8) (Error || error{EndOfStream})!void {
55 const amt_read = try self.readAll(buf);55 const amt_read = try self.readAll(buf);
56 if (amt_read < buf.len) return error.EndOfStream;56 if (amt_read < buf.len) return error.EndOfStream;
57 }57 }
...@@ -71,7 +71,7 @@ pub fn Reader(...@@ -71,7 +71,7 @@ pub fn Reader(
71 array_list: *std.ArrayListAligned(u8, alignment),71 array_list: *std.ArrayListAligned(u8, alignment),
72 max_append_size: usize,72 max_append_size: usize,
73 ) !void {73 ) !void {
74 try array_list.ensureTotalCapacity(math.min(max_append_size, 4096));74 try array_list.ensureTotalCapacity(@min(max_append_size, 4096));
75 const original_len = array_list.items.len;75 const original_len = array_list.items.len;
76 var start_index: usize = original_len;76 var start_index: usize = original_len;
77 while (true) {77 while (true) {
...@@ -103,9 +103,10 @@ pub fn Reader(...@@ -103,9 +103,10 @@ pub fn Reader(
103 var array_list = std.ArrayList(u8).init(allocator);103 var array_list = std.ArrayList(u8).init(allocator);
104 defer array_list.deinit();104 defer array_list.deinit();
105 try self.readAllArrayList(&array_list, max_size);105 try self.readAllArrayList(&array_list, max_size);
106 return array_list.toOwnedSlice();106 return try array_list.toOwnedSlice();
107 }107 }
108108
109 /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
109 /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.110 /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.
110 /// Does not include the delimiter in the result.111 /// Does not include the delimiter in the result.
111 /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the112 /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
...@@ -117,21 +118,10 @@ pub fn Reader(...@@ -117,21 +118,10 @@ pub fn Reader(
117 max_size: usize,118 max_size: usize,
118 ) !void {119 ) !void {
119 array_list.shrinkRetainingCapacity(0);120 array_list.shrinkRetainingCapacity(0);
120 while (true) {121 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
121 if (array_list.items.len == max_size) {
122 return error.StreamTooLong;
123 }
124
125 var byte: u8 = try self.readByte();
126
127 if (byte == delimiter) {
128 return;
129 }
130
131 try array_list.append(byte);
132 }
133 }122 }
134123
124 /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
135 /// Allocates enough memory to read until `delimiter`. If the allocated125 /// Allocates enough memory to read until `delimiter`. If the allocated
136 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.126 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
137 /// Caller owns returned memory.127 /// Caller owns returned memory.
...@@ -144,10 +134,11 @@ pub fn Reader(...@@ -144,10 +134,11 @@ pub fn Reader(
144 ) ![]u8 {134 ) ![]u8 {
145 var array_list = std.ArrayList(u8).init(allocator);135 var array_list = std.ArrayList(u8).init(allocator);
146 defer array_list.deinit();136 defer array_list.deinit();
147 try self.readUntilDelimiterArrayList(&array_list, delimiter, max_size);137 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
148 return array_list.toOwnedSlice();138 return try array_list.toOwnedSlice();
149 }139 }
150140
141 /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
151 /// Reads from the stream until specified byte is found. If the buffer is not142 /// Reads from the stream until specified byte is found. If the buffer is not
152 /// large enough to hold the entire contents, `error.StreamTooLong` is returned.143 /// large enough to hold the entire contents, `error.StreamTooLong` is returned.
153 /// If end-of-stream is found, `error.EndOfStream` is returned.144 /// If end-of-stream is found, `error.EndOfStream` is returned.
...@@ -155,19 +146,14 @@ pub fn Reader(...@@ -155,19 +146,14 @@ pub fn Reader(
155 /// delimiter byte is written to the output buffer but is not included146 /// delimiter byte is written to the output buffer but is not included
156 /// in the returned slice.147 /// in the returned slice.
157 pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 {148 pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 {
158 var index: usize = 0;149 var fbs = std.io.fixedBufferStream(buf);
159 while (true) {150 try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len);
160 if (index >= buf.len) return error.StreamTooLong;151 const output = fbs.getWritten();
161152 buf[output.len] = delimiter; // emulating old behaviour
162 const byte = try self.readByte();153 return output;
163 buf[index] = byte;
164
165 if (byte == delimiter) return buf[0..index];
166
167 index += 1;
168 }
169 }154 }
170155
156 /// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead.
171 /// Allocates enough memory to read until `delimiter` or end-of-stream.157 /// Allocates enough memory to read until `delimiter` or end-of-stream.
172 /// If the allocated memory would be greater than `max_size`, returns158 /// If the allocated memory would be greater than `max_size`, returns
173 /// `error.StreamTooLong`. If end-of-stream is found, returns the rest159 /// `error.StreamTooLong`. If end-of-stream is found, returns the rest
...@@ -183,17 +169,16 @@ pub fn Reader(...@@ -183,17 +169,16 @@ pub fn Reader(
183 ) !?[]u8 {169 ) !?[]u8 {
184 var array_list = std.ArrayList(u8).init(allocator);170 var array_list = std.ArrayList(u8).init(allocator);
185 defer array_list.deinit();171 defer array_list.deinit();
186 self.readUntilDelimiterArrayList(&array_list, delimiter, max_size) catch |err| switch (err) {172 self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) {
187 error.EndOfStream => if (array_list.items.len == 0) {173 error.EndOfStream => if (array_list.items.len == 0) {
188 return null;174 return null;
189 } else {
190 return try array_list.toOwnedSlice();
191 },175 },
192 else => |e| return e,176 else => |e| return e,
193 };177 };
194 return try array_list.toOwnedSlice();178 return try array_list.toOwnedSlice();
195 }179 }
196180
181 /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
197 /// Reads from the stream until specified byte is found. If the buffer is not182 /// Reads from the stream until specified byte is found. If the buffer is not
198 /// large enough to hold the entire contents, `error.StreamTooLong` is returned.183 /// large enough to hold the entire contents, `error.StreamTooLong` is returned.
199 /// If end-of-stream is found, returns the rest of the stream. If this184 /// If end-of-stream is found, returns the rest of the stream. If this
...@@ -202,32 +187,46 @@ pub fn Reader(...@@ -202,32 +187,46 @@ pub fn Reader(
202 /// delimiter byte is written to the output buffer but is not included187 /// delimiter byte is written to the output buffer but is not included
203 /// in the returned slice.188 /// in the returned slice.
204 pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 {189 pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 {
205 var index: usize = 0;190 var fbs = std.io.fixedBufferStream(buf);
206 while (true) {191 self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) {
207 if (index >= buf.len) return error.StreamTooLong;192 error.EndOfStream => if (fbs.getWritten().len == 0) {
208193 return null;
209 const byte = self.readByte() catch |err| switch (err) {194 },
210 error.EndOfStream => {
211 if (index == 0) {
212 return null;
213 } else {
214 return buf[0..index];
215 }
216 },
217 else => |e| return e,
218 };
219 buf[index] = byte;
220195
221 if (byte == delimiter) return buf[0..index];196 else => |e| return e,
197 };
198 const output = fbs.getWritten();
199 buf[output.len] = delimiter; // emulating old behaviour
200 return output;
201 }
222202
223 index += 1;203 /// Appends to the `writer` contents by reading from the stream until `delimiter` is found.
204 /// Does not write the delimiter itself.
205 /// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,
206 /// returns `error.StreamTooLong` and finishes appending.
207 /// If `optional_max_size` is null, appending is unbounded.
208 pub fn streamUntilDelimiter(self: Self, writer: anytype, delimiter: u8, optional_max_size: ?usize) (Error || error{ EndOfStream, StreamTooLong } || @TypeOf(writer).Error)!void {
209 if (optional_max_size) |max_size| {
210 for (0..max_size) |_| {
211 const byte: u8 = try self.readByte(); // (Error || error{EndOfStream})
212 if (byte == delimiter) return;
213 try writer.writeByte(byte); // @TypeOf(writer).Error
214 }
215 return error.StreamTooLong;
216 } else {
217 while (true) {
218 const byte: u8 = try self.readByte(); // (Error || error{EndOfStream})
219 if (byte == delimiter) return;
220 try writer.writeByte(byte); // @TypeOf(writer).Error
221 }
222 // Can not throw `error.StreamTooLong` since there are no boundary.
224 }223 }
225 }224 }
226225
227 /// Reads from the stream until specified byte is found, discarding all data,226 /// Reads from the stream until specified byte is found, discarding all data,
228 /// including the delimiter.227 /// including the delimiter.
229 /// If end-of-stream is found, this function succeeds.228 /// If end-of-stream is found, this function succeeds.
230 pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) !void {229 pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) Error!void {
231 while (true) {230 while (true) {
232 const byte = self.readByte() catch |err| switch (err) {231 const byte = self.readByte() catch |err| switch (err) {
233 error.EndOfStream => return,232 error.EndOfStream => return,
...@@ -238,7 +237,7 @@ pub fn Reader(...@@ -238,7 +237,7 @@ pub fn Reader(
238 }237 }
239238
240 /// Reads 1 byte from the stream or returns `error.EndOfStream`.239 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
241 pub fn readByte(self: Self) !u8 {240 pub fn readByte(self: Self) (Error || error{EndOfStream})!u8 {
242 var result: [1]u8 = undefined;241 var result: [1]u8 = undefined;
243 const amt_read = try self.read(result[0..]);242 const amt_read = try self.read(result[0..]);
244 if (amt_read < 1) return error.EndOfStream;243 if (amt_read < 1) return error.EndOfStream;
...@@ -246,13 +245,13 @@ pub fn Reader(...@@ -246,13 +245,13 @@ pub fn Reader(
246 }245 }
247246
248 /// Same as `readByte` except the returned byte is signed.247 /// Same as `readByte` except the returned byte is signed.
249 pub fn readByteSigned(self: Self) !i8 {248 pub fn readByteSigned(self: Self) (Error || error{EndOfStream})!i8 {
250 return @bitCast(i8, try self.readByte());249 return @bitCast(i8, try self.readByte());
251 }250 }
252251
253 /// Reads exactly `num_bytes` bytes and returns as an array.252 /// Reads exactly `num_bytes` bytes and returns as an array.
254 /// `num_bytes` must be comptime-known253 /// `num_bytes` must be comptime-known
255 pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) ![num_bytes]u8 {254 pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) (Error || error{EndOfStream})![num_bytes]u8 {
256 var bytes: [num_bytes]u8 = undefined;255 var bytes: [num_bytes]u8 = undefined;
257 try self.readNoEof(&bytes);256 try self.readNoEof(&bytes);
258 return bytes;257 return bytes;
...@@ -264,7 +263,7 @@ pub fn Reader(...@@ -264,7 +263,7 @@ pub fn Reader(
264 self: Self,263 self: Self,
265 comptime num_bytes: usize,264 comptime num_bytes: usize,
266 bounded: *std.BoundedArray(u8, num_bytes),265 bounded: *std.BoundedArray(u8, num_bytes),
267 ) !void {266 ) Error!void {
268 while (bounded.len < num_bytes) {267 while (bounded.len < num_bytes) {
269 const bytes_read = try self.read(bounded.unusedCapacitySlice());268 const bytes_read = try self.read(bounded.unusedCapacitySlice());
270 if (bytes_read == 0) return;269 if (bytes_read == 0) return;
...@@ -273,20 +272,20 @@ pub fn Reader(...@@ -273,20 +272,20 @@ pub fn Reader(
273 }272 }
274273
275 /// Reads at most `num_bytes` and returns as a bounded array.274 /// Reads at most `num_bytes` and returns as a bounded array.
276 pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) !std.BoundedArray(u8, num_bytes) {275 pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) Error!std.BoundedArray(u8, num_bytes) {
277 var result = std.BoundedArray(u8, num_bytes){};276 var result = std.BoundedArray(u8, num_bytes){};
278 try self.readIntoBoundedBytes(num_bytes, &result);277 try self.readIntoBoundedBytes(num_bytes, &result);
279 return result;278 return result;
280 }279 }
281280
282 /// Reads a native-endian integer281 /// Reads a native-endian integer
283 pub fn readIntNative(self: Self, comptime T: type) !T {282 pub fn readIntNative(self: Self, comptime T: type) (Error || error{EndOfStream})!T {
284 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);283 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);
285 return mem.readIntNative(T, &bytes);284 return mem.readIntNative(T, &bytes);
286 }285 }
287286
288 /// Reads a foreign-endian integer287 /// Reads a foreign-endian integer
289 pub fn readIntForeign(self: Self, comptime T: type) !T {288 pub fn readIntForeign(self: Self, comptime T: type) (Error || error{EndOfStream})!T {
290 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);289 const bytes = try self.readBytesNoEof((@typeInfo(T).Int.bits + 7) / 8);
291 return mem.readIntForeign(T, &bytes);290 return mem.readIntForeign(T, &bytes);
292 }291 }
...@@ -361,7 +360,7 @@ pub fn Reader(...@@ -361,7 +360,7 @@ pub fn Reader(
361 }360 }
362361
363 /// Reads an integer with the same size as the given enum's tag type. If the integer matches362 /// Reads an integer with the same size as the given enum's tag type. If the integer matches
364 /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an error.363 /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`.
365 /// TODO optimization taking advantage of most fields being in order364 /// TODO optimization taking advantage of most fields being in order
366 pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) !Enum {365 pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) !Enum {
367 const E = error{366 const E = error{
...@@ -710,3 +709,22 @@ test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer"...@@ -710,3 +709,22 @@ test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer"
710 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));709 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
711 try std.testing.expectEqualStrings("12345", &buf);710 try std.testing.expectEqualStrings("12345", &buf);
712}711}
712
713test "Reader.streamUntilDelimiter writes all bytes without delimiter to the output" {
714 const input_string = "some_string_with_delimiter!";
715 var input_fbs = std.io.fixedBufferStream(input_string);
716 const reader = input_fbs.reader();
717
718 var output: [input_string.len]u8 = undefined;
719 var output_fbs = std.io.fixedBufferStream(&output);
720 const writer = output_fbs.writer();
721
722 try reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len);
723 try std.testing.expectEqualStrings("some_string_with_delimiter", output_fbs.getWritten());
724 try std.testing.expectError(error.EndOfStream, reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len));
725
726 input_fbs.reset();
727 output_fbs.reset();
728
729 try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5));
730}