authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-04 11:21:19-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-10-04 11:21:19-07:00
loga306bfcd8eec166906ce2839bc7ff86ba6335376
treed1ae1ef50bb25e5a54829bcff658d922caa40edc
parent4d1e8ef1eb1217e0a997b9bfa164142d83b01c0a
parent9a09651019b24a32945f73dd7a69562f2cf31581
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17344 from ziglang/type-erased-reader

std: add type-erased reader; base GenericReader on it

8 files changed, 1022 insertions(+), 764 deletions(-)

CMakeLists.txt+1-1
...@@ -265,7 +265,7 @@ set(ZIG_STAGE2_SOURCES...@@ -265,7 +265,7 @@ set(ZIG_STAGE2_SOURCES
265 "${CMAKE_SOURCE_DIR}/lib/std/io/find_byte_writer.zig"265 "${CMAKE_SOURCE_DIR}/lib/std/io/find_byte_writer.zig"
266 "${CMAKE_SOURCE_DIR}/lib/std/io/fixed_buffer_stream.zig"266 "${CMAKE_SOURCE_DIR}/lib/std/io/fixed_buffer_stream.zig"
267 "${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig"267 "${CMAKE_SOURCE_DIR}/lib/std/io/limited_reader.zig"
268 "${CMAKE_SOURCE_DIR}/lib/std/io/reader.zig"268 "${CMAKE_SOURCE_DIR}/lib/std/io/Reader.zig"
269 "${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig"269 "${CMAKE_SOURCE_DIR}/lib/std/io/seekable_stream.zig"
270 "${CMAKE_SOURCE_DIR}/lib/std/io/writer.zig"270 "${CMAKE_SOURCE_DIR}/lib/std/io/writer.zig"
271 "${CMAKE_SOURCE_DIR}/lib/std/json.zig"271 "${CMAKE_SOURCE_DIR}/lib/std/json.zig"
lib/std/Build/Step/CheckObject.zig+2-2
...@@ -539,7 +539,7 @@ const MachODumper = struct {...@@ -539,7 +539,7 @@ const MachODumper = struct {
539 strings: []const u8,539 strings: []const u8,
540 };540 };
541541
542 fn parseAndDump(step: *Step, bytes: []align(@alignOf(u64)) const u8) ![]const u8 {542 fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 {
543 const gpa = step.owner.allocator;543 const gpa = step.owner.allocator;
544 var stream = std.io.fixedBufferStream(bytes);544 var stream = std.io.fixedBufferStream(bytes);
545 const reader = stream.reader();545 const reader = stream.reader();
...@@ -556,7 +556,7 @@ const MachODumper = struct {...@@ -556,7 +556,7 @@ const MachODumper = struct {
556 var sections = std.ArrayList(macho.section_64).init(gpa);556 var sections = std.ArrayList(macho.section_64).init(gpa);
557 var imports = std.ArrayList([]const u8).init(gpa);557 var imports = std.ArrayList([]const u8).init(gpa);
558558
559 var it = LoadCommandIterator{559 var it: LoadCommandIterator = .{
560 .ncmds = hdr.ncmds,560 .ncmds = hdr.ncmds,
561 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],561 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
562 };562 };
lib/std/io.zig+252-3
...@@ -10,6 +10,7 @@ const fs = std.fs;...@@ -10,6 +10,7 @@ const fs = std.fs;
10const mem = std.mem;10const mem = std.mem;
11const meta = std.meta;11const meta = std.meta;
12const File = std.fs.File;12const File = std.fs.File;
13const Allocator = std.mem.Allocator;
1314
14pub const Mode = enum {15pub const Mode = enum {
15 /// I/O operates normally, waiting for the operating system syscalls to complete.16 /// I/O operates normally, waiting for the operating system syscalls to complete.
...@@ -105,7 +106,255 @@ pub fn getStdIn() File {...@@ -105,7 +106,255 @@ pub fn getStdIn() File {
105 };106 };
106}107}
107108
108pub const Reader = @import("io/reader.zig").Reader;109pub fn GenericReader(
110 comptime Context: type,
111 comptime ReadError: type,
112 /// Returns the number of bytes read. It may be less than buffer.len.
113 /// If the number of bytes read is 0, it means end of stream.
114 /// End of stream is not an error condition.
115 comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize,
116) type {
117 return struct {
118 context: Context,
119
120 pub const Error = ReadError;
121 pub const NoEofError = ReadError || error{
122 EndOfStream,
123 };
124
125 pub inline fn read(self: Self, buffer: []u8) Error!usize {
126 return readFn(self.context, buffer);
127 }
128
129 pub inline fn readAll(self: Self, buffer: []u8) Error!usize {
130 return @errorCast(self.any().readAll(buffer));
131 }
132
133 pub inline fn readAtLeast(self: Self, buffer: []u8, len: usize) Error!usize {
134 return @errorCast(self.any().readAtLeast(buffer, len));
135 }
136
137 pub inline fn readNoEof(self: Self, buf: []u8) NoEofError!void {
138 return @errorCast(self.any().readNoEof(buf));
139 }
140
141 pub inline fn readAllArrayList(
142 self: Self,
143 array_list: *std.ArrayList(u8),
144 max_append_size: usize,
145 ) (error{StreamTooLong} || Error)!void {
146 return @errorCast(self.any().readAllArrayList(array_list, max_append_size));
147 }
148
149 pub inline fn readAllArrayListAligned(
150 self: Self,
151 comptime alignment: ?u29,
152 array_list: *std.ArrayListAligned(u8, alignment),
153 max_append_size: usize,
154 ) (error{StreamTooLong} || Error)!void {
155 return @errorCast(self.any().readAllArrayListAligned(
156 alignment,
157 array_list,
158 max_append_size,
159 ));
160 }
161
162 pub inline fn readAllAlloc(
163 self: Self,
164 allocator: Allocator,
165 max_size: usize,
166 ) (Error || error{StreamTooLong})![]u8 {
167 return @errorCast(self.any().readAllAlloc(allocator, max_size));
168 }
169
170 pub inline fn readUntilDelimiterArrayList(
171 self: Self,
172 array_list: *std.ArrayList(u8),
173 delimiter: u8,
174 max_size: usize,
175 ) (NoEofError || error{StreamTooLong})!void {
176 return @errorCast(self.any().readUntilDelimiterArrayList(
177 array_list,
178 delimiter,
179 max_size,
180 ));
181 }
182
183 pub inline fn readUntilDelimiterAlloc(
184 self: Self,
185 allocator: Allocator,
186 delimiter: u8,
187 max_size: usize,
188 ) (NoEofError || error{StreamTooLong})![]u8 {
189 return @errorCast(self.any().readUntilDelimiterAlloc(
190 allocator,
191 delimiter,
192 max_size,
193 ));
194 }
195
196 pub inline fn readUntilDelimiter(
197 self: Self,
198 buf: []u8,
199 delimiter: u8,
200 ) (NoEofError || error{StreamTooLong})![]u8 {
201 return @errorCast(self.any().readUntilDelimiter(buf, delimiter));
202 }
203
204 pub inline fn readUntilDelimiterOrEofAlloc(
205 self: Self,
206 allocator: Allocator,
207 delimiter: u8,
208 max_size: usize,
209 ) (Error || error{StreamTooLong})!?[]u8 {
210 return @errorCast(self.any().readUntilDelimiterOrEofAlloc(
211 allocator,
212 delimiter,
213 max_size,
214 ));
215 }
216
217 pub inline fn readUntilDelimiterOrEof(
218 self: Self,
219 buf: []u8,
220 delimiter: u8,
221 ) (Error || error{StreamTooLong})!?[]u8 {
222 return @errorCast(self.any().readUntilDelimiterOrEof(buf, delimiter));
223 }
224
225 pub inline fn streamUntilDelimiter(
226 self: Self,
227 writer: anytype,
228 delimiter: u8,
229 optional_max_size: ?usize,
230 ) (NoEofError || error{StreamTooLong} || @TypeOf(writer).Error)!void {
231 return @errorCast(self.any().streamUntilDelimiter(
232 writer,
233 delimiter,
234 optional_max_size,
235 ));
236 }
237
238 pub inline fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) Error!void {
239 return @errorCast(self.any().skipUntilDelimiterOrEof(delimiter));
240 }
241
242 pub inline fn readByte(self: Self) NoEofError!u8 {
243 return @errorCast(self.any().readByte());
244 }
245
246 pub inline fn readByteSigned(self: Self) NoEofError!i8 {
247 return @errorCast(self.any().readByteSigned());
248 }
249
250 pub inline fn readBytesNoEof(
251 self: Self,
252 comptime num_bytes: usize,
253 ) NoEofError![num_bytes]u8 {
254 return @errorCast(self.any().readBytesNoEof(num_bytes));
255 }
256
257 pub inline fn readIntoBoundedBytes(
258 self: Self,
259 comptime num_bytes: usize,
260 bounded: *std.BoundedArray(u8, num_bytes),
261 ) Error!void {
262 return @errorCast(self.any().readIntoBoundedBytes(num_bytes, bounded));
263 }
264
265 pub inline fn readBoundedBytes(
266 self: Self,
267 comptime num_bytes: usize,
268 ) Error!std.BoundedArray(u8, num_bytes) {
269 return @errorCast(self.any().readBoundedBytes(num_bytes));
270 }
271
272 pub inline fn readIntNative(self: Self, comptime T: type) NoEofError!T {
273 return @errorCast(self.any().readIntNative(T));
274 }
275
276 pub inline fn readIntForeign(self: Self, comptime T: type) NoEofError!T {
277 return @errorCast(self.any().readIntForeign(T));
278 }
279
280 pub inline fn readIntLittle(self: Self, comptime T: type) NoEofError!T {
281 return @errorCast(self.any().readIntLittle(T));
282 }
283
284 pub inline fn readIntBig(self: Self, comptime T: type) NoEofError!T {
285 return @errorCast(self.any().readIntBig(T));
286 }
287
288 pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) NoEofError!T {
289 return @errorCast(self.any().readInt(T, endian));
290 }
291
292 pub inline fn readVarInt(
293 self: Self,
294 comptime ReturnType: type,
295 endian: std.builtin.Endian,
296 size: usize,
297 ) NoEofError!ReturnType {
298 return @errorCast(self.any().readVarInt(ReturnType, endian, size));
299 }
300
301 pub const SkipBytesOptions = AnyReader.SkipBytesOptions;
302
303 pub inline fn skipBytes(
304 self: Self,
305 num_bytes: u64,
306 comptime options: SkipBytesOptions,
307 ) NoEofError!void {
308 return @errorCast(self.any().skipBytes(num_bytes, options));
309 }
310
311 pub inline fn isBytes(self: Self, slice: []const u8) Error!bool {
312 return @errorCast(self.any().isBytes(slice));
313 }
314
315 pub inline fn readStruct(self: Self, comptime T: type) NoEofError!T {
316 return @errorCast(self.any().readStruct(T));
317 }
318
319 pub inline fn readStructBig(self: Self, comptime T: type) NoEofError!T {
320 return @errorCast(self.any().readStructBig(T));
321 }
322
323 pub const ReadEnumError = Error || error{
324 /// An integer was read, but it did not match any of the tags in the supplied enum.
325 InvalidValue,
326 };
327
328 pub inline fn readEnum(
329 self: Self,
330 comptime Enum: type,
331 endian: std.builtin.Endian,
332 ) ReadEnumError!Enum {
333 return @errorCast(self.any().readEnum(Enum, endian));
334 }
335
336 pub inline fn any(self: *const Self) AnyReader {
337 return .{
338 .context = @ptrCast(&self.context),
339 .readFn = typeErasedReadFn,
340 };
341 }
342
343 const Self = @This();
344
345 fn typeErasedReadFn(context: *const anyopaque, buffer: []u8) anyerror!usize {
346 const ptr: *const Context = @alignCast(@ptrCast(context));
347 return readFn(ptr.*, buffer);
348 }
349 };
350}
351
352/// Deprecated; consider switching to `AnyReader` or use `GenericReader`
353/// to use previous API.
354pub const Reader = GenericReader;
355
356pub const AnyReader = @import("io/Reader.zig");
357
109pub const Writer = @import("io/writer.zig").Writer;358pub const Writer = @import("io/writer.zig").Writer;
110pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;359pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
111360
...@@ -168,7 +417,7 @@ test "null_writer" {...@@ -168,7 +417,7 @@ test "null_writer" {
168}417}
169418
170pub fn poll(419pub fn poll(
171 allocator: std.mem.Allocator,420 allocator: Allocator,
172 comptime StreamEnum: type,421 comptime StreamEnum: type,
173 files: PollFiles(StreamEnum),422 files: PollFiles(StreamEnum),
174) Poller(StreamEnum) {423) Poller(StreamEnum) {
...@@ -418,6 +667,7 @@ pub fn PollFiles(comptime StreamEnum: type) type {...@@ -418,6 +667,7 @@ pub fn PollFiles(comptime StreamEnum: type) type {
418}667}
419668
420test {669test {
670 _ = AnyReader;
421 _ = @import("io/bit_reader.zig");671 _ = @import("io/bit_reader.zig");
422 _ = @import("io/bit_writer.zig");672 _ = @import("io/bit_writer.zig");
423 _ = @import("io/buffered_atomic_file.zig");673 _ = @import("io/buffered_atomic_file.zig");
...@@ -427,7 +677,6 @@ test {...@@ -427,7 +677,6 @@ test {
427 _ = @import("io/counting_writer.zig");677 _ = @import("io/counting_writer.zig");
428 _ = @import("io/counting_reader.zig");678 _ = @import("io/counting_reader.zig");
429 _ = @import("io/fixed_buffer_stream.zig");679 _ = @import("io/fixed_buffer_stream.zig");
430 _ = @import("io/reader.zig");
431 _ = @import("io/writer.zig");680 _ = @import("io/writer.zig");
432 _ = @import("io/peek_stream.zig");681 _ = @import("io/peek_stream.zig");
433 _ = @import("io/seekable_stream.zig");682 _ = @import("io/seekable_stream.zig");
lib/std/io/Reader.zig created+395
...@@ -0,0 +1,395 @@
1context: *const anyopaque,
2readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize,
3
4pub const Error = anyerror;
5
6/// Returns the number of bytes read. It may be less than buffer.len.
7/// If the number of bytes read is 0, it means end of stream.
8/// End of stream is not an error condition.
9pub fn read(self: Self, buffer: []u8) anyerror!usize {
10 return self.readFn(self.context, buffer);
11}
12
13/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
14/// means the stream reached the end. Reaching the end of a stream is not an error
15/// condition.
16pub fn readAll(self: Self, buffer: []u8) anyerror!usize {
17 return readAtLeast(self, buffer, buffer.len);
18}
19
20/// Returns the number of bytes read, calling the underlying read
21/// function the minimal number of times until the buffer has at least
22/// `len` bytes filled. If the number read is less than `len` it means
23/// the stream reached the end. Reaching the end of the stream is not
24/// an error condition.
25pub fn readAtLeast(self: Self, buffer: []u8, len: usize) anyerror!usize {
26 assert(len <= buffer.len);
27 var index: usize = 0;
28 while (index < len) {
29 const amt = try self.read(buffer[index..]);
30 if (amt == 0) break;
31 index += amt;
32 }
33 return index;
34}
35
36/// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
37pub fn readNoEof(self: Self, buf: []u8) anyerror!void {
38 const amt_read = try self.readAll(buf);
39 if (amt_read < buf.len) return error.EndOfStream;
40}
41
42/// Appends to the `std.ArrayList` contents by reading from the stream
43/// until end of stream is found.
44/// If the number of bytes appended would exceed `max_append_size`,
45/// `error.StreamTooLong` is returned
46/// and the `std.ArrayList` has exactly `max_append_size` bytes appended.
47pub fn readAllArrayList(
48 self: Self,
49 array_list: *std.ArrayList(u8),
50 max_append_size: usize,
51) anyerror!void {
52 return self.readAllArrayListAligned(null, array_list, max_append_size);
53}
54
55pub fn readAllArrayListAligned(
56 self: Self,
57 comptime alignment: ?u29,
58 array_list: *std.ArrayListAligned(u8, alignment),
59 max_append_size: usize,
60) anyerror!void {
61 try array_list.ensureTotalCapacity(@min(max_append_size, 4096));
62 const original_len = array_list.items.len;
63 var start_index: usize = original_len;
64 while (true) {
65 array_list.expandToCapacity();
66 const dest_slice = array_list.items[start_index..];
67 const bytes_read = try self.readAll(dest_slice);
68 start_index += bytes_read;
69
70 if (start_index - original_len > max_append_size) {
71 array_list.shrinkAndFree(original_len + max_append_size);
72 return error.StreamTooLong;
73 }
74
75 if (bytes_read != dest_slice.len) {
76 array_list.shrinkAndFree(start_index);
77 return;
78 }
79
80 // This will trigger ArrayList to expand superlinearly at whatever its growth rate is.
81 try array_list.ensureTotalCapacity(start_index + 1);
82 }
83}
84
85/// Allocates enough memory to hold all the contents of the stream. If the allocated
86/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
87/// Caller owns returned memory.
88/// If this function returns an error, the contents from the stream read so far are lost.
89pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) anyerror![]u8 {
90 var array_list = std.ArrayList(u8).init(allocator);
91 defer array_list.deinit();
92 try self.readAllArrayList(&array_list, max_size);
93 return try array_list.toOwnedSlice();
94}
95
96/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
97/// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.
98/// Does not include the delimiter in the result.
99/// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
100/// `std.ArrayList` is populated with `max_size` bytes from the stream.
101pub fn readUntilDelimiterArrayList(
102 self: Self,
103 array_list: *std.ArrayList(u8),
104 delimiter: u8,
105 max_size: usize,
106) anyerror!void {
107 array_list.shrinkRetainingCapacity(0);
108 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
109}
110
111/// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
112/// Allocates enough memory to read until `delimiter`. If the allocated
113/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
114/// Caller owns returned memory.
115/// If this function returns an error, the contents from the stream read so far are lost.
116pub fn readUntilDelimiterAlloc(
117 self: Self,
118 allocator: mem.Allocator,
119 delimiter: u8,
120 max_size: usize,
121) anyerror![]u8 {
122 var array_list = std.ArrayList(u8).init(allocator);
123 defer array_list.deinit();
124 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
125 return try array_list.toOwnedSlice();
126}
127
128/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
129/// Reads from the stream until specified byte is found. If the buffer is not
130/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
131/// If end-of-stream is found, `error.EndOfStream` is returned.
132/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
133/// delimiter byte is written to the output buffer but is not included
134/// in the returned slice.
135pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) anyerror![]u8 {
136 var fbs = std.io.fixedBufferStream(buf);
137 try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len);
138 const output = fbs.getWritten();
139 buf[output.len] = delimiter; // emulating old behaviour
140 return output;
141}
142
143/// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead.
144/// Allocates enough memory to read until `delimiter` or end-of-stream.
145/// If the allocated memory would be greater than `max_size`, returns
146/// `error.StreamTooLong`. If end-of-stream is found, returns the rest
147/// of the stream. If this function is called again after that, returns
148/// null.
149/// Caller owns returned memory.
150/// If this function returns an error, the contents from the stream read so far are lost.
151pub fn readUntilDelimiterOrEofAlloc(
152 self: Self,
153 allocator: mem.Allocator,
154 delimiter: u8,
155 max_size: usize,
156) anyerror!?[]u8 {
157 var array_list = std.ArrayList(u8).init(allocator);
158 defer array_list.deinit();
159 self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) {
160 error.EndOfStream => if (array_list.items.len == 0) {
161 return null;
162 },
163 else => |e| return e,
164 };
165 return try array_list.toOwnedSlice();
166}
167
168/// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
169/// Reads from the stream until specified byte is found. If the buffer is not
170/// large enough to hold the entire contents, `error.StreamTooLong` is returned.
171/// If end-of-stream is found, returns the rest of the stream. If this
172/// function is called again after that, returns null.
173/// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
174/// delimiter byte is written to the output buffer but is not included
175/// in the returned slice.
176pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) anyerror!?[]u8 {
177 var fbs = std.io.fixedBufferStream(buf);
178 self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) {
179 error.EndOfStream => if (fbs.getWritten().len == 0) {
180 return null;
181 },
182
183 else => |e| return e,
184 };
185 const output = fbs.getWritten();
186 buf[output.len] = delimiter; // emulating old behaviour
187 return output;
188}
189
190/// Appends to the `writer` contents by reading from the stream until `delimiter` is found.
191/// Does not write the delimiter itself.
192/// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,
193/// returns `error.StreamTooLong` and finishes appending.
194/// If `optional_max_size` is null, appending is unbounded.
195pub fn streamUntilDelimiter(
196 self: Self,
197 writer: anytype,
198 delimiter: u8,
199 optional_max_size: ?usize,
200) anyerror!void {
201 if (optional_max_size) |max_size| {
202 for (0..max_size) |_| {
203 const byte: u8 = try self.readByte();
204 if (byte == delimiter) return;
205 try writer.writeByte(byte);
206 }
207 return error.StreamTooLong;
208 } else {
209 while (true) {
210 const byte: u8 = try self.readByte();
211 if (byte == delimiter) return;
212 try writer.writeByte(byte);
213 }
214 // Can not throw `error.StreamTooLong` since there are no boundary.
215 }
216}
217
218/// Reads from the stream until specified byte is found, discarding all data,
219/// including the delimiter.
220/// If end-of-stream is found, this function succeeds.
221pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void {
222 while (true) {
223 const byte = self.readByte() catch |err| switch (err) {
224 error.EndOfStream => return,
225 else => |e| return e,
226 };
227 if (byte == delimiter) return;
228 }
229}
230
231/// Reads 1 byte from the stream or returns `error.EndOfStream`.
232pub fn readByte(self: Self) anyerror!u8 {
233 var result: [1]u8 = undefined;
234 const amt_read = try self.read(result[0..]);
235 if (amt_read < 1) return error.EndOfStream;
236 return result[0];
237}
238
239/// Same as `readByte` except the returned byte is signed.
240pub fn readByteSigned(self: Self) anyerror!i8 {
241 return @as(i8, @bitCast(try self.readByte()));
242}
243
244/// Reads exactly `num_bytes` bytes and returns as an array.
245/// `num_bytes` must be comptime-known
246pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 {
247 var bytes: [num_bytes]u8 = undefined;
248 try self.readNoEof(&bytes);
249 return bytes;
250}
251
252/// Reads bytes until `bounded.len` is equal to `num_bytes`,
253/// or the stream ends.
254///
255/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()`
256pub fn readIntoBoundedBytes(
257 self: Self,
258 comptime num_bytes: usize,
259 bounded: *std.BoundedArray(u8, num_bytes),
260) anyerror!void {
261 while (bounded.len < num_bytes) {
262 // get at most the number of bytes free in the bounded array
263 const bytes_read = try self.read(bounded.unusedCapacitySlice());
264 if (bytes_read == 0) return;
265
266 // bytes_read will never be larger than @TypeOf(bounded.len)
267 // due to `self.read` being bounded by `bounded.unusedCapacitySlice()`
268 bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read));
269 }
270}
271
272/// Reads at most `num_bytes` and returns as a bounded array.
273pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) {
274 var result = std.BoundedArray(u8, num_bytes){};
275 try self.readIntoBoundedBytes(num_bytes, &result);
276 return result;
277}
278
279/// Reads a native-endian integer
280pub fn readIntNative(self: Self, comptime T: type) anyerror!T {
281 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
282 return mem.readIntNative(T, &bytes);
283}
284
285/// Reads a foreign-endian integer
286pub fn readIntForeign(self: Self, comptime T: type) anyerror!T {
287 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
288 return mem.readIntForeign(T, &bytes);
289}
290
291pub fn readIntLittle(self: Self, comptime T: type) anyerror!T {
292 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
293 return mem.readIntLittle(T, &bytes);
294}
295
296pub fn readIntBig(self: Self, comptime T: type) anyerror!T {
297 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
298 return mem.readIntBig(T, &bytes);
299}
300
301pub fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
302 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
303 return mem.readInt(T, &bytes, endian);
304}
305
306pub fn readVarInt(
307 self: Self,
308 comptime ReturnType: type,
309 endian: std.builtin.Endian,
310 size: usize,
311) anyerror!ReturnType {
312 assert(size <= @sizeOf(ReturnType));
313 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
314 const bytes = bytes_buf[0..size];
315 try self.readNoEof(bytes);
316 return mem.readVarInt(ReturnType, bytes, endian);
317}
318
319/// Optional parameters for `skipBytes`
320pub const SkipBytesOptions = struct {
321 buf_size: usize = 512,
322};
323
324// `num_bytes` is a `u64` to match `off_t`
325/// Reads `num_bytes` bytes from the stream and discards them
326pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) anyerror!void {
327 var buf: [options.buf_size]u8 = undefined;
328 var remaining = num_bytes;
329
330 while (remaining > 0) {
331 const amt = @min(remaining, options.buf_size);
332 try self.readNoEof(buf[0..amt]);
333 remaining -= amt;
334 }
335}
336
337/// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice
338pub fn isBytes(self: Self, slice: []const u8) anyerror!bool {
339 var i: usize = 0;
340 var matches = true;
341 while (i < slice.len) : (i += 1) {
342 if (slice[i] != try self.readByte()) {
343 matches = false;
344 }
345 }
346 return matches;
347}
348
349pub fn readStruct(self: Self, comptime T: type) anyerror!T {
350 // Only extern and packed structs have defined in-memory layout.
351 comptime assert(@typeInfo(T).Struct.layout != .Auto);
352 var res: [1]T = undefined;
353 try self.readNoEof(mem.sliceAsBytes(res[0..]));
354 return res[0];
355}
356
357pub fn readStructBig(self: Self, comptime T: type) anyerror!T {
358 var res = try self.readStruct(T);
359 if (native_endian != std.builtin.Endian.Big) {
360 mem.byteSwapAllFields(T, &res);
361 }
362 return res;
363}
364
365/// Reads an integer with the same size as the given enum's tag type. If the integer matches
366/// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`.
367/// TODO optimization taking advantage of most fields being in order
368pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum {
369 const E = error{
370 /// An integer was read, but it did not match any of the tags in the supplied enum.
371 InvalidValue,
372 };
373 const type_info = @typeInfo(Enum).Enum;
374 const tag = try self.readInt(type_info.tag_type, endian);
375
376 inline for (std.meta.fields(Enum)) |field| {
377 if (tag == field.value) {
378 return @field(Enum, field.name);
379 }
380 }
381
382 return E.InvalidValue;
383}
384
385const std = @import("../std.zig");
386const Self = @This();
387const math = std.math;
388const assert = std.debug.assert;
389const mem = std.mem;
390const testing = std.testing;
391const native_endian = @import("builtin").target.cpu.arch.endian();
392
393test {
394 _ = @import("Reader/test.zig");
395}
lib/std/io/Reader/test.zig created+371
...@@ -0,0 +1,371 @@
1const std = @import("../../std.zig");
2const testing = std.testing;
3
4test "Reader" {
5 var buf = "a\x02".*;
6 var fis = std.io.fixedBufferStream(&buf);
7 const reader = fis.reader();
8 try testing.expect((try reader.readByte()) == 'a');
9 try testing.expect((try reader.readEnum(enum(u8) {
10 a = 0,
11 b = 99,
12 c = 2,
13 d = 3,
14 }, undefined)) == .c);
15 try testing.expectError(error.EndOfStream, reader.readByte());
16}
17
18test "Reader.isBytes" {
19 var fis = std.io.fixedBufferStream("foobar");
20 const reader = fis.reader();
21 try testing.expectEqual(true, try reader.isBytes("foo"));
22 try testing.expectEqual(false, try reader.isBytes("qux"));
23}
24
25test "Reader.skipBytes" {
26 var fis = std.io.fixedBufferStream("foobar");
27 const reader = fis.reader();
28 try reader.skipBytes(3, .{});
29 try testing.expect(try reader.isBytes("bar"));
30 try reader.skipBytes(0, .{});
31 try testing.expectError(error.EndOfStream, reader.skipBytes(1, .{}));
32}
33
34test "Reader.readUntilDelimiterArrayList returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
35 const a = std.testing.allocator;
36 var list = std.ArrayList(u8).init(a);
37 defer list.deinit();
38
39 var fis = std.io.fixedBufferStream("0000\n1234\n");
40 const reader = fis.reader();
41
42 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
43 try std.testing.expectEqualStrings("0000", list.items);
44 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
45 try std.testing.expectEqualStrings("1234", list.items);
46 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
47}
48
49test "Reader.readUntilDelimiterArrayList returns an empty ArrayList" {
50 const a = std.testing.allocator;
51 var list = std.ArrayList(u8).init(a);
52 defer list.deinit();
53
54 var fis = std.io.fixedBufferStream("\n");
55 const reader = fis.reader();
56
57 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
58 try std.testing.expectEqualStrings("", list.items);
59}
60
61test "Reader.readUntilDelimiterArrayList returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
62 const a = std.testing.allocator;
63 var list = std.ArrayList(u8).init(a);
64 defer list.deinit();
65
66 var fis = std.io.fixedBufferStream("1234567\n");
67 const reader = fis.reader();
68
69 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterArrayList(&list, '\n', 5));
70 try std.testing.expectEqualStrings("12345", list.items);
71 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
72 try std.testing.expectEqualStrings("67", list.items);
73}
74
75test "Reader.readUntilDelimiterArrayList returns EndOfStream" {
76 const a = std.testing.allocator;
77 var list = std.ArrayList(u8).init(a);
78 defer list.deinit();
79
80 var fis = std.io.fixedBufferStream("1234");
81 const reader = fis.reader();
82
83 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
84 try std.testing.expectEqualStrings("1234", list.items);
85}
86
87test "Reader.readUntilDelimiterAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
88 const a = std.testing.allocator;
89
90 var fis = std.io.fixedBufferStream("0000\n1234\n");
91 const reader = fis.reader();
92
93 {
94 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
95 defer a.free(result);
96 try std.testing.expectEqualStrings("0000", result);
97 }
98
99 {
100 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
101 defer a.free(result);
102 try std.testing.expectEqualStrings("1234", result);
103 }
104
105 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
106}
107
108test "Reader.readUntilDelimiterAlloc returns an empty ArrayList" {
109 const a = std.testing.allocator;
110
111 var fis = std.io.fixedBufferStream("\n");
112 const reader = fis.reader();
113
114 {
115 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
116 defer a.free(result);
117 try std.testing.expectEqualStrings("", result);
118 }
119}
120
121test "Reader.readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
122 const a = std.testing.allocator;
123
124 var fis = std.io.fixedBufferStream("1234567\n");
125 const reader = fis.reader();
126
127 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterAlloc(a, '\n', 5));
128
129 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
130 defer a.free(result);
131 try std.testing.expectEqualStrings("67", result);
132}
133
134test "Reader.readUntilDelimiterAlloc returns EndOfStream" {
135 const a = std.testing.allocator;
136
137 var fis = std.io.fixedBufferStream("1234");
138 const reader = fis.reader();
139
140 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
141}
142
143test "Reader.readUntilDelimiter returns bytes read until the delimiter" {
144 var buf: [5]u8 = undefined;
145 var fis = std.io.fixedBufferStream("0000\n1234\n");
146 const reader = fis.reader();
147 try std.testing.expectEqualStrings("0000", try reader.readUntilDelimiter(&buf, '\n'));
148 try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
149}
150
151test "Reader.readUntilDelimiter returns an empty string" {
152 var buf: [5]u8 = undefined;
153 var fis = std.io.fixedBufferStream("\n");
154 const reader = fis.reader();
155 try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
156}
157
158test "Reader.readUntilDelimiter returns StreamTooLong, then an empty string" {
159 var buf: [5]u8 = undefined;
160 var fis = std.io.fixedBufferStream("12345\n");
161 const reader = fis.reader();
162 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
163 try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
164}
165
166test "Reader.readUntilDelimiter returns StreamTooLong, then bytes read until the delimiter" {
167 var buf: [5]u8 = undefined;
168 var fis = std.io.fixedBufferStream("1234567\n");
169 const reader = fis.reader();
170 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
171 try std.testing.expectEqualStrings("67", try reader.readUntilDelimiter(&buf, '\n'));
172}
173
174test "Reader.readUntilDelimiter returns EndOfStream" {
175 {
176 var buf: [5]u8 = undefined;
177 var fis = std.io.fixedBufferStream("");
178 const reader = fis.reader();
179 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
180 }
181 {
182 var buf: [5]u8 = undefined;
183 var fis = std.io.fixedBufferStream("1234");
184 const reader = fis.reader();
185 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
186 }
187}
188
189test "Reader.readUntilDelimiter returns bytes read until delimiter, then EndOfStream" {
190 var buf: [5]u8 = undefined;
191 var fis = std.io.fixedBufferStream("1234\n");
192 const reader = fis.reader();
193 try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
194 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
195}
196
197test "Reader.readUntilDelimiter returns StreamTooLong, then EndOfStream" {
198 var buf: [5]u8 = undefined;
199 var fis = std.io.fixedBufferStream("12345");
200 const reader = fis.reader();
201 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
202 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
203}
204
205test "Reader.readUntilDelimiter writes all bytes read to the output buffer" {
206 var buf: [5]u8 = undefined;
207 var fis = std.io.fixedBufferStream("0000\n12345");
208 const reader = fis.reader();
209 _ = try reader.readUntilDelimiter(&buf, '\n');
210 try std.testing.expectEqualStrings("0000\n", &buf);
211 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
212 try std.testing.expectEqualStrings("12345", &buf);
213}
214
215test "Reader.readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
216 const a = std.testing.allocator;
217
218 var fis = std.io.fixedBufferStream("0000\n1234\n");
219 const reader = fis.reader();
220
221 {
222 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
223 defer a.free(result);
224 try std.testing.expectEqualStrings("0000", result);
225 }
226
227 {
228 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
229 defer a.free(result);
230 try std.testing.expectEqualStrings("1234", result);
231 }
232
233 try std.testing.expect((try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)) == null);
234}
235
236test "Reader.readUntilDelimiterOrEofAlloc returns an empty ArrayList" {
237 const a = std.testing.allocator;
238
239 var fis = std.io.fixedBufferStream("\n");
240 const reader = fis.reader();
241
242 {
243 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
244 defer a.free(result);
245 try std.testing.expectEqualStrings("", result);
246 }
247}
248
249test "Reader.readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
250 const a = std.testing.allocator;
251
252 var fis = std.io.fixedBufferStream("1234567\n");
253 const reader = fis.reader();
254
255 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEofAlloc(a, '\n', 5));
256
257 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
258 defer a.free(result);
259 try std.testing.expectEqualStrings("67", result);
260}
261
262test "Reader.readUntilDelimiterOrEof returns bytes read until the delimiter" {
263 var buf: [5]u8 = undefined;
264 var fis = std.io.fixedBufferStream("0000\n1234\n");
265 const reader = fis.reader();
266 try std.testing.expectEqualStrings("0000", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
267 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
268}
269
270test "Reader.readUntilDelimiterOrEof returns an empty string" {
271 var buf: [5]u8 = undefined;
272 var fis = std.io.fixedBufferStream("\n");
273 const reader = fis.reader();
274 try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
275}
276
277test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then an empty string" {
278 var buf: [5]u8 = undefined;
279 var fis = std.io.fixedBufferStream("12345\n");
280 const reader = fis.reader();
281 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
282 try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
283}
284
285test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until the delimiter" {
286 var buf: [5]u8 = undefined;
287 var fis = std.io.fixedBufferStream("1234567\n");
288 const reader = fis.reader();
289 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
290 try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
291}
292
293test "Reader.readUntilDelimiterOrEof returns null" {
294 var buf: [5]u8 = undefined;
295 var fis = std.io.fixedBufferStream("");
296 const reader = fis.reader();
297 try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
298}
299
300test "Reader.readUntilDelimiterOrEof returns bytes read until delimiter, then null" {
301 var buf: [5]u8 = undefined;
302 var fis = std.io.fixedBufferStream("1234\n");
303 const reader = fis.reader();
304 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
305 try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
306}
307
308test "Reader.readUntilDelimiterOrEof returns bytes read until end-of-stream" {
309 var buf: [5]u8 = undefined;
310 var fis = std.io.fixedBufferStream("1234");
311 const reader = fis.reader();
312 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
313}
314
315test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until end-of-stream" {
316 var buf: [5]u8 = undefined;
317 var fis = std.io.fixedBufferStream("1234567");
318 const reader = fis.reader();
319 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
320 try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
321}
322
323test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" {
324 var buf: [5]u8 = undefined;
325 var fis = std.io.fixedBufferStream("0000\n12345");
326 const reader = fis.reader();
327 _ = try reader.readUntilDelimiterOrEof(&buf, '\n');
328 try std.testing.expectEqualStrings("0000\n", &buf);
329 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
330 try std.testing.expectEqualStrings("12345", &buf);
331}
332
333test "Reader.streamUntilDelimiter writes all bytes without delimiter to the output" {
334 const input_string = "some_string_with_delimiter!";
335 var input_fbs = std.io.fixedBufferStream(input_string);
336 const reader = input_fbs.reader();
337
338 var output: [input_string.len]u8 = undefined;
339 var output_fbs = std.io.fixedBufferStream(&output);
340 const writer = output_fbs.writer();
341
342 try reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len);
343 try std.testing.expectEqualStrings("some_string_with_delimiter", output_fbs.getWritten());
344 try std.testing.expectError(error.EndOfStream, reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len));
345
346 input_fbs.reset();
347 output_fbs.reset();
348
349 try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5));
350}
351
352test "Reader.readBoundedBytes correctly reads into a new bounded array" {
353 const test_string = "abcdefg";
354 var fis = std.io.fixedBufferStream(test_string);
355 const reader = fis.reader();
356
357 var array = try reader.readBoundedBytes(10000);
358 try testing.expectEqualStrings(array.slice(), test_string);
359}
360
361test "Reader.readIntoBoundedBytes correctly reads into a provided bounded array" {
362 const test_string = "abcdefg";
363 var fis = std.io.fixedBufferStream(test_string);
364 const reader = fis.reader();
365
366 var bounded_array = std.BoundedArray(u8, 10000){};
367
368 // compile time error if the size is not the same at the provided `bounded.capacity()`
369 try reader.readIntoBoundedBytes(10000, &bounded_array);
370 try testing.expectEqualStrings(bounded_array.slice(), test_string);
371}
lib/std/io/reader.zig deleted-757
...@@ -1,757 +0,0 @@
1const std = @import("../std.zig");
2const math = std.math;
3const assert = std.debug.assert;
4const mem = std.mem;
5const testing = std.testing;
6const native_endian = @import("builtin").target.cpu.arch.endian();
7
8pub fn Reader(
9 comptime Context: type,
10 comptime ReadError: type,
11 /// Returns the number of bytes read. It may be less than buffer.len.
12 /// If the number of bytes read is 0, it means end of stream.
13 /// End of stream is not an error condition.
14 comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize,
15) type {
16 return struct {
17 pub const Error = ReadError;
18
19 context: Context,
20
21 const Self = @This();
22
23 /// Returns the number of bytes read. It may be less than buffer.len.
24 /// If the number of bytes read is 0, it means end of stream.
25 /// End of stream is not an error condition.
26 pub fn read(self: Self, buffer: []u8) Error!usize {
27 return readFn(self.context, buffer);
28 }
29
30 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
31 /// means the stream reached the end. Reaching the end of a stream is not an error
32 /// condition.
33 pub fn readAll(self: Self, buffer: []u8) Error!usize {
34 return readAtLeast(self, buffer, buffer.len);
35 }
36
37 /// Returns the number of bytes read, calling the underlying read
38 /// function the minimal number of times until the buffer has at least
39 /// `len` bytes filled. If the number read is less than `len` it means
40 /// the stream reached the end. Reaching the end of the stream is not
41 /// an error condition.
42 pub fn readAtLeast(self: Self, buffer: []u8, len: usize) Error!usize {
43 assert(len <= buffer.len);
44 var index: usize = 0;
45 while (index < len) {
46 const amt = try self.read(buffer[index..]);
47 if (amt == 0) break;
48 index += amt;
49 }
50 return index;
51 }
52
53 /// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
54 pub fn readNoEof(self: Self, buf: []u8) (Error || error{EndOfStream})!void {
55 const amt_read = try self.readAll(buf);
56 if (amt_read < buf.len) return error.EndOfStream;
57 }
58
59 /// Appends to the `std.ArrayList` contents by reading from the stream
60 /// until end of stream is found.
61 /// If the number of bytes appended would exceed `max_append_size`,
62 /// `error.StreamTooLong` is returned
63 /// and the `std.ArrayList` has exactly `max_append_size` bytes appended.
64 pub fn readAllArrayList(self: Self, array_list: *std.ArrayList(u8), max_append_size: usize) !void {
65 return self.readAllArrayListAligned(null, array_list, max_append_size);
66 }
67
68 pub fn readAllArrayListAligned(
69 self: Self,
70 comptime alignment: ?u29,
71 array_list: *std.ArrayListAligned(u8, alignment),
72 max_append_size: usize,
73 ) !void {
74 try array_list.ensureTotalCapacity(@min(max_append_size, 4096));
75 const original_len = array_list.items.len;
76 var start_index: usize = original_len;
77 while (true) {
78 array_list.expandToCapacity();
79 const dest_slice = array_list.items[start_index..];
80 const bytes_read = try self.readAll(dest_slice);
81 start_index += bytes_read;
82
83 if (start_index - original_len > max_append_size) {
84 array_list.shrinkAndFree(original_len + max_append_size);
85 return error.StreamTooLong;
86 }
87
88 if (bytes_read != dest_slice.len) {
89 array_list.shrinkAndFree(start_index);
90 return;
91 }
92
93 // This will trigger ArrayList to expand superlinearly at whatever its growth rate is.
94 try array_list.ensureTotalCapacity(start_index + 1);
95 }
96 }
97
98 /// Allocates enough memory to hold all the contents of the stream. If the allocated
99 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
100 /// Caller owns returned memory.
101 /// If this function returns an error, the contents from the stream read so far are lost.
102 pub fn readAllAlloc(self: Self, allocator: mem.Allocator, max_size: usize) ![]u8 {
103 var array_list = std.ArrayList(u8).init(allocator);
104 defer array_list.deinit();
105 try self.readAllArrayList(&array_list, max_size);
106 return try array_list.toOwnedSlice();
107 }
108
109 /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
110 /// Replaces the `std.ArrayList` contents by reading from the stream until `delimiter` is found.
111 /// Does not include the delimiter in the result.
112 /// If the `std.ArrayList` length would exceed `max_size`, `error.StreamTooLong` is returned and the
113 /// `std.ArrayList` is populated with `max_size` bytes from the stream.
114 pub fn readUntilDelimiterArrayList(
115 self: Self,
116 array_list: *std.ArrayList(u8),
117 delimiter: u8,
118 max_size: usize,
119 ) !void {
120 array_list.shrinkRetainingCapacity(0);
121 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
122 }
123
124 /// Deprecated: use `streamUntilDelimiter` with ArrayList's writer instead.
125 /// Allocates enough memory to read until `delimiter`. If the allocated
126 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
127 /// Caller owns returned memory.
128 /// If this function returns an error, the contents from the stream read so far are lost.
129 pub fn readUntilDelimiterAlloc(
130 self: Self,
131 allocator: mem.Allocator,
132 delimiter: u8,
133 max_size: usize,
134 ) ![]u8 {
135 var array_list = std.ArrayList(u8).init(allocator);
136 defer array_list.deinit();
137 try self.streamUntilDelimiter(array_list.writer(), delimiter, max_size);
138 return try array_list.toOwnedSlice();
139 }
140
141 /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
142 /// Reads from the stream until specified byte is found. If the buffer is not
143 /// large enough to hold the entire contents, `error.StreamTooLong` is returned.
144 /// If end-of-stream is found, `error.EndOfStream` is returned.
145 /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
146 /// delimiter byte is written to the output buffer but is not included
147 /// in the returned slice.
148 pub fn readUntilDelimiter(self: Self, buf: []u8, delimiter: u8) ![]u8 {
149 var fbs = std.io.fixedBufferStream(buf);
150 try self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len);
151 const output = fbs.getWritten();
152 buf[output.len] = delimiter; // emulating old behaviour
153 return output;
154 }
155
156 /// Deprecated: use `streamUntilDelimiter` with ArrayList's (or any other's) writer instead.
157 /// Allocates enough memory to read until `delimiter` or end-of-stream.
158 /// If the allocated memory would be greater than `max_size`, returns
159 /// `error.StreamTooLong`. If end-of-stream is found, returns the rest
160 /// of the stream. If this function is called again after that, returns
161 /// null.
162 /// Caller owns returned memory.
163 /// If this function returns an error, the contents from the stream read so far are lost.
164 pub fn readUntilDelimiterOrEofAlloc(
165 self: Self,
166 allocator: mem.Allocator,
167 delimiter: u8,
168 max_size: usize,
169 ) !?[]u8 {
170 var array_list = std.ArrayList(u8).init(allocator);
171 defer array_list.deinit();
172 self.streamUntilDelimiter(array_list.writer(), delimiter, max_size) catch |err| switch (err) {
173 error.EndOfStream => if (array_list.items.len == 0) {
174 return null;
175 },
176 else => |e| return e,
177 };
178 return try array_list.toOwnedSlice();
179 }
180
181 /// Deprecated: use `streamUntilDelimiter` with FixedBufferStream's writer instead.
182 /// Reads from the stream until specified byte is found. If the buffer is not
183 /// large enough to hold the entire contents, `error.StreamTooLong` is returned.
184 /// If end-of-stream is found, returns the rest of the stream. If this
185 /// function is called again after that, returns null.
186 /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The
187 /// delimiter byte is written to the output buffer but is not included
188 /// in the returned slice.
189 pub fn readUntilDelimiterOrEof(self: Self, buf: []u8, delimiter: u8) !?[]u8 {
190 var fbs = std.io.fixedBufferStream(buf);
191 self.streamUntilDelimiter(fbs.writer(), delimiter, fbs.buffer.len) catch |err| switch (err) {
192 error.EndOfStream => if (fbs.getWritten().len == 0) {
193 return null;
194 },
195
196 else => |e| return e,
197 };
198 const output = fbs.getWritten();
199 buf[output.len] = delimiter; // emulating old behaviour
200 return output;
201 }
202
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.
223 }
224 }
225
226 /// Reads from the stream until specified byte is found, discarding all data,
227 /// including the delimiter.
228 /// If end-of-stream is found, this function succeeds.
229 pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) Error!void {
230 while (true) {
231 const byte = self.readByte() catch |err| switch (err) {
232 error.EndOfStream => return,
233 else => |e| return e,
234 };
235 if (byte == delimiter) return;
236 }
237 }
238
239 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
240 pub fn readByte(self: Self) (Error || error{EndOfStream})!u8 {
241 var result: [1]u8 = undefined;
242 const amt_read = try self.read(result[0..]);
243 if (amt_read < 1) return error.EndOfStream;
244 return result[0];
245 }
246
247 /// Same as `readByte` except the returned byte is signed.
248 pub fn readByteSigned(self: Self) (Error || error{EndOfStream})!i8 {
249 return @as(i8, @bitCast(try self.readByte()));
250 }
251
252 /// Reads exactly `num_bytes` bytes and returns as an array.
253 /// `num_bytes` must be comptime-known
254 pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) (Error || error{EndOfStream})![num_bytes]u8 {
255 var bytes: [num_bytes]u8 = undefined;
256 try self.readNoEof(&bytes);
257 return bytes;
258 }
259
260 /// Reads bytes until `bounded.len` is equal to `num_bytes`,
261 /// or the stream ends.
262 ///
263 /// * it is assumed that `num_bytes` will not exceed `bounded.capacity()`
264 pub fn readIntoBoundedBytes(
265 self: Self,
266 comptime num_bytes: usize,
267 bounded: *std.BoundedArray(u8, num_bytes),
268 ) Error!void {
269 while (bounded.len < num_bytes) {
270 // get at most the number of bytes free in the bounded array
271 const bytes_read = try self.read(bounded.unusedCapacitySlice());
272 if (bytes_read == 0) return;
273
274 // bytes_read will never be larger than @TypeOf(bounded.len)
275 // due to `self.read` being bounded by `bounded.unusedCapacitySlice()`
276 bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read));
277 }
278 }
279
280 /// Reads at most `num_bytes` and returns as a bounded array.
281 pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) Error!std.BoundedArray(u8, num_bytes) {
282 var result = std.BoundedArray(u8, num_bytes){};
283 try self.readIntoBoundedBytes(num_bytes, &result);
284 return result;
285 }
286
287 /// Reads a native-endian integer
288 pub fn readIntNative(self: Self, comptime T: type) (Error || error{EndOfStream})!T {
289 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
290 return mem.readIntNative(T, &bytes);
291 }
292
293 /// Reads a foreign-endian integer
294 pub fn readIntForeign(self: Self, comptime T: type) (Error || error{EndOfStream})!T {
295 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
296 return mem.readIntForeign(T, &bytes);
297 }
298
299 pub fn readIntLittle(self: Self, comptime T: type) !T {
300 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
301 return mem.readIntLittle(T, &bytes);
302 }
303
304 pub fn readIntBig(self: Self, comptime T: type) !T {
305 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
306 return mem.readIntBig(T, &bytes);
307 }
308
309 pub fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) !T {
310 const bytes = try self.readBytesNoEof(@as(u16, @intCast((@as(u17, @typeInfo(T).Int.bits) + 7) / 8)));
311 return mem.readInt(T, &bytes, endian);
312 }
313
314 pub fn readVarInt(self: Self, comptime ReturnType: type, endian: std.builtin.Endian, size: usize) !ReturnType {
315 assert(size <= @sizeOf(ReturnType));
316 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
317 const bytes = bytes_buf[0..size];
318 try self.readNoEof(bytes);
319 return mem.readVarInt(ReturnType, bytes, endian);
320 }
321
322 /// Optional parameters for `skipBytes`
323 pub const SkipBytesOptions = struct {
324 buf_size: usize = 512,
325 };
326
327 // `num_bytes` is a `u64` to match `off_t`
328 /// Reads `num_bytes` bytes from the stream and discards them
329 pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) !void {
330 var buf: [options.buf_size]u8 = undefined;
331 var remaining = num_bytes;
332
333 while (remaining > 0) {
334 const amt = @min(remaining, options.buf_size);
335 try self.readNoEof(buf[0..amt]);
336 remaining -= amt;
337 }
338 }
339
340 /// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice
341 pub fn isBytes(self: Self, slice: []const u8) !bool {
342 var i: usize = 0;
343 var matches = true;
344 while (i < slice.len) : (i += 1) {
345 if (slice[i] != try self.readByte()) {
346 matches = false;
347 }
348 }
349 return matches;
350 }
351
352 pub fn readStruct(self: Self, comptime T: type) !T {
353 // Only extern and packed structs have defined in-memory layout.
354 comptime assert(@typeInfo(T).Struct.layout != .Auto);
355 var res: [1]T = undefined;
356 try self.readNoEof(mem.sliceAsBytes(res[0..]));
357 return res[0];
358 }
359
360 pub fn readStructBig(self: Self, comptime T: type) !T {
361 var res = try self.readStruct(T);
362 if (native_endian != std.builtin.Endian.Big) {
363 mem.byteSwapAllFields(T, &res);
364 }
365 return res;
366 }
367
368 /// Reads an integer with the same size as the given enum's tag type. If the integer matches
369 /// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`.
370 /// TODO optimization taking advantage of most fields being in order
371 pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) !Enum {
372 const E = error{
373 /// An integer was read, but it did not match any of the tags in the supplied enum.
374 InvalidValue,
375 };
376 const type_info = @typeInfo(Enum).Enum;
377 const tag = try self.readInt(type_info.tag_type, endian);
378
379 inline for (std.meta.fields(Enum)) |field| {
380 if (tag == field.value) {
381 return @field(Enum, field.name);
382 }
383 }
384
385 return E.InvalidValue;
386 }
387 };
388}
389
390test "Reader" {
391 var buf = "a\x02".*;
392 var fis = std.io.fixedBufferStream(&buf);
393 const reader = fis.reader();
394 try testing.expect((try reader.readByte()) == 'a');
395 try testing.expect((try reader.readEnum(enum(u8) {
396 a = 0,
397 b = 99,
398 c = 2,
399 d = 3,
400 }, undefined)) == .c);
401 try testing.expectError(error.EndOfStream, reader.readByte());
402}
403
404test "Reader.isBytes" {
405 var fis = std.io.fixedBufferStream("foobar");
406 const reader = fis.reader();
407 try testing.expectEqual(true, try reader.isBytes("foo"));
408 try testing.expectEqual(false, try reader.isBytes("qux"));
409}
410
411test "Reader.skipBytes" {
412 var fis = std.io.fixedBufferStream("foobar");
413 const reader = fis.reader();
414 try reader.skipBytes(3, .{});
415 try testing.expect(try reader.isBytes("bar"));
416 try reader.skipBytes(0, .{});
417 try testing.expectError(error.EndOfStream, reader.skipBytes(1, .{}));
418}
419
420test "Reader.readUntilDelimiterArrayList returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
421 const a = std.testing.allocator;
422 var list = std.ArrayList(u8).init(a);
423 defer list.deinit();
424
425 var fis = std.io.fixedBufferStream("0000\n1234\n");
426 const reader = fis.reader();
427
428 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
429 try std.testing.expectEqualStrings("0000", list.items);
430 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
431 try std.testing.expectEqualStrings("1234", list.items);
432 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
433}
434
435test "Reader.readUntilDelimiterArrayList returns an empty ArrayList" {
436 const a = std.testing.allocator;
437 var list = std.ArrayList(u8).init(a);
438 defer list.deinit();
439
440 var fis = std.io.fixedBufferStream("\n");
441 const reader = fis.reader();
442
443 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
444 try std.testing.expectEqualStrings("", list.items);
445}
446
447test "Reader.readUntilDelimiterArrayList returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
448 const a = std.testing.allocator;
449 var list = std.ArrayList(u8).init(a);
450 defer list.deinit();
451
452 var fis = std.io.fixedBufferStream("1234567\n");
453 const reader = fis.reader();
454
455 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterArrayList(&list, '\n', 5));
456 try std.testing.expectEqualStrings("12345", list.items);
457 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
458 try std.testing.expectEqualStrings("67", list.items);
459}
460
461test "Reader.readUntilDelimiterArrayList returns EndOfStream" {
462 const a = std.testing.allocator;
463 var list = std.ArrayList(u8).init(a);
464 defer list.deinit();
465
466 var fis = std.io.fixedBufferStream("1234");
467 const reader = fis.reader();
468
469 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
470 try std.testing.expectEqualStrings("1234", list.items);
471}
472
473test "Reader.readUntilDelimiterAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
474 const a = std.testing.allocator;
475
476 var fis = std.io.fixedBufferStream("0000\n1234\n");
477 const reader = fis.reader();
478
479 {
480 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
481 defer a.free(result);
482 try std.testing.expectEqualStrings("0000", result);
483 }
484
485 {
486 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
487 defer a.free(result);
488 try std.testing.expectEqualStrings("1234", result);
489 }
490
491 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
492}
493
494test "Reader.readUntilDelimiterAlloc returns an empty ArrayList" {
495 const a = std.testing.allocator;
496
497 var fis = std.io.fixedBufferStream("\n");
498 const reader = fis.reader();
499
500 {
501 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
502 defer a.free(result);
503 try std.testing.expectEqualStrings("", result);
504 }
505}
506
507test "Reader.readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
508 const a = std.testing.allocator;
509
510 var fis = std.io.fixedBufferStream("1234567\n");
511 const reader = fis.reader();
512
513 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterAlloc(a, '\n', 5));
514
515 var result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
516 defer a.free(result);
517 try std.testing.expectEqualStrings("67", result);
518}
519
520test "Reader.readUntilDelimiterAlloc returns EndOfStream" {
521 const a = std.testing.allocator;
522
523 var fis = std.io.fixedBufferStream("1234");
524 const reader = fis.reader();
525
526 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
527}
528
529test "Reader.readUntilDelimiter returns bytes read until the delimiter" {
530 var buf: [5]u8 = undefined;
531 var fis = std.io.fixedBufferStream("0000\n1234\n");
532 const reader = fis.reader();
533 try std.testing.expectEqualStrings("0000", try reader.readUntilDelimiter(&buf, '\n'));
534 try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
535}
536
537test "Reader.readUntilDelimiter returns an empty string" {
538 var buf: [5]u8 = undefined;
539 var fis = std.io.fixedBufferStream("\n");
540 const reader = fis.reader();
541 try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
542}
543
544test "Reader.readUntilDelimiter returns StreamTooLong, then an empty string" {
545 var buf: [5]u8 = undefined;
546 var fis = std.io.fixedBufferStream("12345\n");
547 const reader = fis.reader();
548 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
549 try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
550}
551
552test "Reader.readUntilDelimiter returns StreamTooLong, then bytes read until the delimiter" {
553 var buf: [5]u8 = undefined;
554 var fis = std.io.fixedBufferStream("1234567\n");
555 const reader = fis.reader();
556 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
557 try std.testing.expectEqualStrings("67", try reader.readUntilDelimiter(&buf, '\n'));
558}
559
560test "Reader.readUntilDelimiter returns EndOfStream" {
561 {
562 var buf: [5]u8 = undefined;
563 var fis = std.io.fixedBufferStream("");
564 const reader = fis.reader();
565 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
566 }
567 {
568 var buf: [5]u8 = undefined;
569 var fis = std.io.fixedBufferStream("1234");
570 const reader = fis.reader();
571 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
572 }
573}
574
575test "Reader.readUntilDelimiter returns bytes read until delimiter, then EndOfStream" {
576 var buf: [5]u8 = undefined;
577 var fis = std.io.fixedBufferStream("1234\n");
578 const reader = fis.reader();
579 try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
580 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
581}
582
583test "Reader.readUntilDelimiter returns StreamTooLong, then EndOfStream" {
584 var buf: [5]u8 = undefined;
585 var fis = std.io.fixedBufferStream("12345");
586 const reader = fis.reader();
587 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
588 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
589}
590
591test "Reader.readUntilDelimiter writes all bytes read to the output buffer" {
592 var buf: [5]u8 = undefined;
593 var fis = std.io.fixedBufferStream("0000\n12345");
594 const reader = fis.reader();
595 _ = try reader.readUntilDelimiter(&buf, '\n');
596 try std.testing.expectEqualStrings("0000\n", &buf);
597 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
598 try std.testing.expectEqualStrings("12345", &buf);
599}
600
601test "Reader.readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
602 const a = std.testing.allocator;
603
604 var fis = std.io.fixedBufferStream("0000\n1234\n");
605 const reader = fis.reader();
606
607 {
608 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
609 defer a.free(result);
610 try std.testing.expectEqualStrings("0000", result);
611 }
612
613 {
614 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
615 defer a.free(result);
616 try std.testing.expectEqualStrings("1234", result);
617 }
618
619 try std.testing.expect((try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)) == null);
620}
621
622test "Reader.readUntilDelimiterOrEofAlloc returns an empty ArrayList" {
623 const a = std.testing.allocator;
624
625 var fis = std.io.fixedBufferStream("\n");
626 const reader = fis.reader();
627
628 {
629 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
630 defer a.free(result);
631 try std.testing.expectEqualStrings("", result);
632 }
633}
634
635test "Reader.readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
636 const a = std.testing.allocator;
637
638 var fis = std.io.fixedBufferStream("1234567\n");
639 const reader = fis.reader();
640
641 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEofAlloc(a, '\n', 5));
642
643 var result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
644 defer a.free(result);
645 try std.testing.expectEqualStrings("67", result);
646}
647
648test "Reader.readUntilDelimiterOrEof returns bytes read until the delimiter" {
649 var buf: [5]u8 = undefined;
650 var fis = std.io.fixedBufferStream("0000\n1234\n");
651 const reader = fis.reader();
652 try std.testing.expectEqualStrings("0000", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
653 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
654}
655
656test "Reader.readUntilDelimiterOrEof returns an empty string" {
657 var buf: [5]u8 = undefined;
658 var fis = std.io.fixedBufferStream("\n");
659 const reader = fis.reader();
660 try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
661}
662
663test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then an empty string" {
664 var buf: [5]u8 = undefined;
665 var fis = std.io.fixedBufferStream("12345\n");
666 const reader = fis.reader();
667 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
668 try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
669}
670
671test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until the delimiter" {
672 var buf: [5]u8 = undefined;
673 var fis = std.io.fixedBufferStream("1234567\n");
674 const reader = fis.reader();
675 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
676 try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
677}
678
679test "Reader.readUntilDelimiterOrEof returns null" {
680 var buf: [5]u8 = undefined;
681 var fis = std.io.fixedBufferStream("");
682 const reader = fis.reader();
683 try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
684}
685
686test "Reader.readUntilDelimiterOrEof returns bytes read until delimiter, then null" {
687 var buf: [5]u8 = undefined;
688 var fis = std.io.fixedBufferStream("1234\n");
689 const reader = fis.reader();
690 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
691 try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
692}
693
694test "Reader.readUntilDelimiterOrEof returns bytes read until end-of-stream" {
695 var buf: [5]u8 = undefined;
696 var fis = std.io.fixedBufferStream("1234");
697 const reader = fis.reader();
698 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
699}
700
701test "Reader.readUntilDelimiterOrEof returns StreamTooLong, then bytes read until end-of-stream" {
702 var buf: [5]u8 = undefined;
703 var fis = std.io.fixedBufferStream("1234567");
704 const reader = fis.reader();
705 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
706 try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
707}
708
709test "Reader.readUntilDelimiterOrEof writes all bytes read to the output buffer" {
710 var buf: [5]u8 = undefined;
711 var fis = std.io.fixedBufferStream("0000\n12345");
712 const reader = fis.reader();
713 _ = try reader.readUntilDelimiterOrEof(&buf, '\n');
714 try std.testing.expectEqualStrings("0000\n", &buf);
715 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
716 try std.testing.expectEqualStrings("12345", &buf);
717}
718
719test "Reader.streamUntilDelimiter writes all bytes without delimiter to the output" {
720 const input_string = "some_string_with_delimiter!";
721 var input_fbs = std.io.fixedBufferStream(input_string);
722 const reader = input_fbs.reader();
723
724 var output: [input_string.len]u8 = undefined;
725 var output_fbs = std.io.fixedBufferStream(&output);
726 const writer = output_fbs.writer();
727
728 try reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len);
729 try std.testing.expectEqualStrings("some_string_with_delimiter", output_fbs.getWritten());
730 try std.testing.expectError(error.EndOfStream, reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len));
731
732 input_fbs.reset();
733 output_fbs.reset();
734
735 try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5));
736}
737
738test "Reader.readBoundedBytes correctly reads into a new bounded array" {
739 const test_string = "abcdefg";
740 var fis = std.io.fixedBufferStream(test_string);
741 const reader = fis.reader();
742
743 var array = try reader.readBoundedBytes(10000);
744 try testing.expectEqualStrings(array.slice(), test_string);
745}
746
747test "Reader.readIntoBoundedBytes correctly reads into a provided bounded array" {
748 const test_string = "abcdefg";
749 var fis = std.io.fixedBufferStream(test_string);
750 const reader = fis.reader();
751
752 var bounded_array = std.BoundedArray(u8, 10000){};
753
754 // compile time error if the size is not the same at the provided `bounded.capacity()`
755 try reader.readIntoBoundedBytes(10000, &bounded_array);
756 try testing.expectEqualStrings(bounded_array.slice(), test_string);
757}
lib/std/macho.zig+1-1
...@@ -1853,7 +1853,7 @@ pub const data_in_code_entry = extern struct {...@@ -1853,7 +1853,7 @@ pub const data_in_code_entry = extern struct {
18531853
1854pub const LoadCommandIterator = struct {1854pub const LoadCommandIterator = struct {
1855 ncmds: usize,1855 ncmds: usize,
1856 buffer: []align(@alignOf(u64)) const u8,1856 buffer: []const u8,
1857 index: usize = 0,1857 index: usize = 0,
18581858
1859 pub const LoadCommand = struct {1859 pub const LoadCommand = struct {
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ