authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-20 17:15:21-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-01 16:35:26-07:00
log5aa8573f2bc8fad297a63ce798ed5ede5b5833ac
tree0a6c831c183c6a4a05659037df19864f416926d6
parent2fb6ce2f92ee79fa4171dccc6cb262db2e91d63a

redo reader


16 files changed, 908 insertions(+), 1536 deletions(-)

CMakeLists.txt-6
......@@ -452,12 +452,6 @@ set(ZIG_STAGE2_SOURCES
452452 lib/std/io.zig
453453 lib/std/io/Reader.zig
454454 lib/std/io/Writer.zig
455 lib/std/io/buffered_atomic_file.zig
456 lib/std/io/change_detection_stream.zig
457 lib/std/io/counting_reader.zig
458 lib/std/io/find_byte_writer.zig
459 lib/std/io/limited_reader.zig
460 lib/std/io/seekable_stream.zig
461455 lib/std/json.zig
462456 lib/std/leb128.zig
463457 lib/std/log.zig
lib/std/fifo.zig-19
......@@ -374,25 +374,6 @@ pub fn LinearFifo(
374374 return self.buf[index];
375375 }
376376
377 /// Pump data from a reader into a writer.
378 /// Stops when reader returns 0 bytes (EOF).
379 /// Buffer size must be set before calling; a buffer length of 0 is invalid.
380 pub fn pump(self: *Self, src_reader: anytype, dest_writer: anytype) !void {
381 assert(self.buf.len > 0);
382 while (true) {
383 if (self.writableLength() > 0) {
384 const n = try src_reader.read(self.writableSlice(0));
385 if (n == 0) break; // EOF
386 self.update(n);
387 }
388 self.discard(try dest_writer.write(self.readableSlice(0)));
389 }
390 // flush remaining data
391 while (self.readableLength() > 0) {
392 self.discard(try dest_writer.write(self.readableSlice(0)));
393 }
394 }
395
396377 pub fn toOwnedSlice(self: *Self) Allocator.Error![]T {
397378 if (self.head != 0) self.realign();
398379 assert(self.head == 0);
lib/std/fs/File.zig+25-4
......@@ -1586,10 +1586,14 @@ fn writeFileAllSendfile(self: File, in_file: File, args: WriteFileOptions) posix
15861586 }
15871587}
15881588
1589pub const Reader = io.Reader(File, ReadError, read);
1590
1591pub fn reader(file: File) Reader {
1592 return .{ .context = file };
1589pub fn reader(file: File) std.io.Reader {
1590 return .{
1591 .context = handleToOpaque(file.handle),
1592 .vtable = .{
1593 .seekRead = reader_seekRead,
1594 .streamRead = reader_streamRead,
1595 },
1596 };
15931597}
15941598
15951599pub fn writer(file: File) std.io.Writer {
......@@ -1606,6 +1610,23 @@ pub fn writer(file: File) std.io.Writer {
16061610/// vectors through the underlying write calls as possible.
16071611const max_buffers_len = 16;
16081612
1613pub fn reader_seekRead(
1614 context: *anyopaque,
1615 bw: *std.io.BufferedWriter,
1616 limit: std.io.Reader.Limit,
1617 offset: u64,
1618) anyerror!usize {
1619 const file = opaqueToHandle(context);
1620 const len: std.io.Writer.Len = if (limit.unwrap()) |l| .init(l) else .entire_file;
1621 return writer.writeFile(bw, file, .init(offset), len, &.{}, 0);
1622}
1623
1624pub fn reader_streamRead(context: *anyopaque, bw: *std.io.BufferedWriter, limit: std.io.Reader.Limit) anyerror!usize {
1625 const file = opaqueToHandle(context);
1626 const len: std.io.Writer.Len = if (limit.unwrap()) |l| .init(l) else .entire_file;
1627 return writer.writeFile(bw, file, .none, len, &.{}, 0);
1628}
1629
16091630pub fn writer_writeSplat(context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize {
16101631 const file = opaqueToHandle(context);
16111632 var splat_buffer: [256]u8 = undefined;
lib/std/io.zig+8-197
......@@ -62,195 +62,14 @@ pub fn getStdIn() File {
6262 return .{ .handle = getStdInHandle() };
6363}
6464
65pub fn GenericReader(
66 comptime Context: type,
67 comptime ReadError: type,
68 /// Returns the number of bytes read. It may be less than buffer.len.
69 /// If the number of bytes read is 0, it means end of stream.
70 /// End of stream is not an error condition.
71 comptime readFn: fn (context: Context, buffer: []u8) ReadError!usize,
72) type {
73 return struct {
74 context: Context,
75
76 pub const Error = ReadError;
77 pub const NoEofError = ReadError || error{
78 EndOfStream,
79 };
80
81 pub inline fn read(self: Self, buffer: []u8) Error!usize {
82 return readFn(self.context, buffer);
83 }
84
85 pub inline fn readAll(self: Self, buffer: []u8) Error!usize {
86 return @errorCast(self.any().readAll(buffer));
87 }
88
89 pub inline fn readAtLeast(self: Self, buffer: []u8, len: usize) Error!usize {
90 return @errorCast(self.any().readAtLeast(buffer, len));
91 }
92
93 pub inline fn readNoEof(self: Self, buf: []u8) NoEofError!void {
94 return @errorCast(self.any().readNoEof(buf));
95 }
96
97 pub inline fn readAllArrayList(
98 self: Self,
99 array_list: *std.ArrayList(u8),
100 max_append_size: usize,
101 ) (error{StreamTooLong} || Allocator.Error || Error)!void {
102 return @errorCast(self.any().readAllArrayList(array_list, max_append_size));
103 }
104
105 pub inline fn readAllArrayListAligned(
106 self: Self,
107 comptime alignment: ?Alignment,
108 array_list: *std.ArrayListAligned(u8, alignment),
109 max_append_size: usize,
110 ) (error{StreamTooLong} || Allocator.Error || Error)!void {
111 return @errorCast(self.any().readAllArrayListAligned(
112 alignment,
113 array_list,
114 max_append_size,
115 ));
116 }
117
118 pub inline fn readAllAlloc(
119 self: Self,
120 allocator: Allocator,
121 max_size: usize,
122 ) (Error || Allocator.Error || error{StreamTooLong})![]u8 {
123 return @errorCast(self.any().readAllAlloc(allocator, max_size));
124 }
125
126 pub inline fn streamUntilDelimiter(
127 self: Self,
128 writer: *std.io.BufferedWriter,
129 delimiter: u8,
130 optional_max_size: ?usize,
131 ) anyerror!void {
132 return self.any().streamUntilDelimiter(
133 writer,
134 delimiter,
135 optional_max_size,
136 );
137 }
138
139 pub inline fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) Error!void {
140 return @errorCast(self.any().skipUntilDelimiterOrEof(delimiter));
141 }
142
143 pub inline fn readByte(self: Self) NoEofError!u8 {
144 return @errorCast(self.any().readByte());
145 }
146
147 pub inline fn readByteSigned(self: Self) NoEofError!i8 {
148 return @errorCast(self.any().readByteSigned());
149 }
150
151 pub inline fn readBytesNoEof(
152 self: Self,
153 comptime num_bytes: usize,
154 ) NoEofError![num_bytes]u8 {
155 return @errorCast(self.any().readBytesNoEof(num_bytes));
156 }
157
158 pub inline fn readIntoBoundedBytes(
159 self: Self,
160 comptime num_bytes: usize,
161 bounded: *std.BoundedArray(u8, num_bytes),
162 ) Error!void {
163 return @errorCast(self.any().readIntoBoundedBytes(num_bytes, bounded));
164 }
165
166 pub inline fn readBoundedBytes(
167 self: Self,
168 comptime num_bytes: usize,
169 ) Error!std.BoundedArray(u8, num_bytes) {
170 return @errorCast(self.any().readBoundedBytes(num_bytes));
171 }
172
173 pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) NoEofError!T {
174 return @errorCast(self.any().readInt(T, endian));
175 }
176
177 pub inline fn readVarInt(
178 self: Self,
179 comptime ReturnType: type,
180 endian: std.builtin.Endian,
181 size: usize,
182 ) NoEofError!ReturnType {
183 return @errorCast(self.any().readVarInt(ReturnType, endian, size));
184 }
185
186 pub const SkipBytesOptions = AnyReader.SkipBytesOptions;
187
188 pub inline fn skipBytes(
189 self: Self,
190 num_bytes: u64,
191 comptime options: SkipBytesOptions,
192 ) NoEofError!void {
193 return @errorCast(self.any().skipBytes(num_bytes, options));
194 }
195
196 pub inline fn isBytes(self: Self, slice: []const u8) NoEofError!bool {
197 return @errorCast(self.any().isBytes(slice));
198 }
199
200 pub inline fn readStruct(self: Self, comptime T: type) NoEofError!T {
201 return @errorCast(self.any().readStruct(T));
202 }
203
204 pub inline fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) NoEofError!T {
205 return @errorCast(self.any().readStructEndian(T, endian));
206 }
207
208 pub const ReadEnumError = NoEofError || error{
209 /// An integer was read, but it did not match any of the tags in the supplied enum.
210 InvalidValue,
211 };
212
213 pub inline fn readEnum(
214 self: Self,
215 comptime Enum: type,
216 endian: std.builtin.Endian,
217 ) ReadEnumError!Enum {
218 return @errorCast(self.any().readEnum(Enum, endian));
219 }
220
221 pub inline fn any(self: *const Self) AnyReader {
222 return .{
223 .context = @ptrCast(&self.context),
224 .readFn = typeErasedReadFn,
225 };
226 }
227
228 const Self = @This();
229
230 fn typeErasedReadFn(context: *const anyopaque, buffer: []u8) anyerror!usize {
231 const ptr: *const Context = @alignCast(@ptrCast(context));
232 return readFn(ptr.*, buffer);
233 }
234 };
235}
236
237/// Deprecated; consider switching to `AnyReader` or use `GenericReader`
238/// to use previous API. To be removed after 0.14.0 is tagged.
239pub const Reader = GenericReader;
65pub const Reader = @import("io/Reader.zig");
24066pub const Writer = @import("io/Writer.zig");
24167
242pub const AnyReader = @import("io/Reader.zig");
243
244pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
245
68pub const BufferedReader = @import("io/BufferedReader.zig");
24669pub const BufferedWriter = @import("io/BufferedWriter.zig");
24770pub const AllocatingWriter = @import("io/AllocatingWriter.zig");
248
249pub const BufferedReader = @import("io/buffered_reader.zig").BufferedReader;
250pub const bufferedReader = @import("io/buffered_reader.zig").bufferedReader;
251pub const bufferedReaderSize = @import("io/buffered_reader.zig").bufferedReaderSize;
252
253pub const FixedBufferStream = @import("io/FixedBufferStream.zig");
71pub const CountingWriter = @import("io/CountingWriter.zig");
72pub const CountingReader = @import("io/CountingReader.zig");
25473
25574pub const CWriter = @import("io/c_writer.zig").CWriter;
25675pub const cWriter = @import("io/c_writer.zig").cWriter;
......@@ -258,10 +77,6 @@ pub const cWriter = @import("io/c_writer.zig").cWriter;
25877pub const LimitedReader = @import("io/limited_reader.zig").LimitedReader;
25978pub const limitedReader = @import("io/limited_reader.zig").limitedReader;
26079
261pub const CountingWriter = @import("io/CountingWriter.zig");
262pub const CountingReader = @import("io/counting_reader.zig").CountingReader;
263pub const countingReader = @import("io/counting_reader.zig").countingReader;
264
26580pub const MultiWriter = @import("io/multi_writer.zig").MultiWriter;
26681pub const multiWriter = @import("io/multi_writer.zig").multiWriter;
26782
......@@ -279,8 +94,6 @@ pub const findByteWriter = @import("io/find_byte_writer.zig").findByteWriter;
27994
28095pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
28196
282pub const StreamSource = @import("io/stream_source.zig").StreamSource;
283
28497pub const tty = @import("io/tty.zig");
28598
28699/// A `Writer` that discards all data.
......@@ -725,18 +538,16 @@ pub fn PollFiles(comptime StreamEnum: type) type {
725538}
726539
727540test {
728 _ = AnyReader;
541 _ = BufferedWriter;
542 _ = BufferedReader;
543 _ = Reader;
729544 _ = Writer;
730545 _ = CountingWriter;
731 _ = FixedBufferStream;
546 _ = CountingReader;
732547 _ = AllocatingWriter;
733548 _ = @import("io/bit_reader.zig");
734549 _ = @import("io/bit_writer.zig");
735550 _ = @import("io/buffered_atomic_file.zig");
736 _ = @import("io/buffered_reader.zig");
737551 _ = @import("io/c_writer.zig");
738 _ = @import("io/counting_reader.zig");
739 _ = @import("io/seekable_stream.zig");
740 _ = @import("io/stream_source.zig");
741552 _ = @import("io/test.zig");
742553}
lib/std/io/BufferedReader.zig created+561
......@@ -0,0 +1,561 @@
1const builtin = @import("builtin");
2const native_endian = builtin.target.cpu.arch.endian();
3
4const std = @import("../std.zig");
5const assert = std.debug.assert;
6const testing = std.testing;
7const BufferedWriter = std.io.BufferedWriter;
8const Reader = std.io.Reader;
9
10const BufferedReader = @This();
11
12/// Number of bytes which have been consumed from `storage`.
13seek: usize,
14storage: BufferedWriter,
15unbuffered_reader: Reader,
16
17pub fn initFixed(br: *BufferedReader, buffer: []const u8) void {
18 br.* = .{
19 .seek = 0,
20 .storage = .{
21 .buffer = buffer,
22 .mode = .fixed,
23 },
24 .reader = .{
25 .context = br,
26 .vtable = &.{
27 .streamRead = null,
28 .seekRead = null,
29 },
30 },
31 };
32}
33
34pub fn deinit(br: *BufferedReader) void {
35 br.storage.deinit();
36 br.* = undefined;
37}
38
39/// Although `BufferedReader` can easily satisfy the `Reader` interface, it's
40/// generally more practical to pass a `BufferedReader` instance itself around,
41/// since it will result in fewer calls across vtable boundaries.
42pub fn reader(br: *BufferedReader) Reader {
43 return .{
44 .context = br,
45 .vtable = &.{
46 .streamRead = passthru_streamRead,
47 .seekRead = passthru_seekRead,
48 },
49 };
50}
51
52fn passthru_streamRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit) anyerror!Reader.Status {
53 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
54 const buffer = br.storage.buffer.items;
55 const buffered = buffer[br.seek..];
56 const limited = buffered[0..limit.min(buffered.len)];
57 if (limited.len > 0) {
58 const n = try bw.writeSplat(limited, 1);
59 br.seek += n;
60 return .{
61 .end = false,
62 .len = @intCast(n),
63 };
64 }
65 return br.unbuffered_reader.streamRead(bw, limit);
66}
67
68fn passthru_seekRead(ctx: *anyopaque, bw: *BufferedWriter, limit: Reader.Limit, off: u64) anyerror!Reader.Status {
69 const br: *BufferedReader = @alignCast(@ptrCast(ctx));
70 const buffer = br.storage.buffer.items;
71 if (off < buffer.len) {
72 const send = buffer[off..limit.min(buffer.len)];
73 return bw.writeSplat(send, 1);
74 }
75 return br.unbuffered_reader.seekRead(bw, limit, off - buffer.len);
76}
77
78/// Returns the next `n` bytes from `unbuffered_reader`, filling the buffer as
79/// necessary.
80///
81/// Invalidates previously returned values from `peek`.
82///
83/// Asserts that the `BufferedReader` was initialized with a buffer capacity at
84/// least as big as `n`.
85///
86/// If there are fewer than `n` bytes left in the stream, `error.EndOfStream`
87/// is returned instead.
88///
89/// See also:
90/// * `toss`
91pub fn peek(br: *BufferedReader, n: usize) anyerror![]u8 {
92 const list = &br.storage.buffer;
93 assert(n <= list.capacity);
94 try fill(br, n);
95 return list.items[br.seek..][0..n];
96}
97
98/// Skips the next `n` bytes from the stream, advancing the seek position. This
99/// is typically and safely used after `peek`.
100///
101/// Asserts that the number of bytes buffered is at least as many as `n`.
102///
103/// See also:
104/// * `peek`.
105/// * `discard`.
106pub fn toss(br: *BufferedReader, n: usize) void {
107 br.seek += n;
108 assert(br.seek <= br.storage.buffer.items.len);
109}
110
111/// Equivalent to `peek` + `toss`.
112pub fn take(br: *BufferedReader, n: usize) anyerror![]u8 {
113 const result = try peek(br, n);
114 toss(br, n);
115 return result;
116}
117
118/// Returns the next `n` bytes from `unbuffered_reader` as an array, filling
119/// the buffer as necessary.
120///
121/// Asserts that the `BufferedReader` was initialized with a buffer capacity at
122/// least as big as `n`.
123///
124/// If there are fewer than `n` bytes left in the stream, `error.EndOfStream`
125/// is returned instead.
126///
127/// See also:
128/// * `take`
129pub fn takeArray(br: *BufferedReader, comptime n: usize) anyerror!*[n]u8 {
130 return (try take(br, n))[0..n];
131}
132
133/// Skips the next `n` bytes from the stream, advancing the seek position.
134///
135/// Unlike `toss` which is infallible, in this function `n` can be any amount.
136///
137/// Returns `error.EndOfStream` if fewer than `n` bytes could be discarded.
138///
139/// See also:
140/// * `toss`
141/// * `discardAll`
142pub fn discard(br: *BufferedReader, n: usize) anyerror!void {
143 const list = &br.storage.buffer;
144 var remaining = n;
145 while (remaining > 0) {
146 const proposed_seek = br.seek + remaining;
147 if (proposed_seek <= list.items.len) {
148 br.seek = proposed_seek;
149 return;
150 }
151 remaining -= (list.items.len - br.seek);
152 list.items.len = 0;
153 br.seek = 0;
154 const status = try br.unbuffered_reader.streamRead(&br.storage, .none);
155 if (remaining <= list.items.len) continue;
156 if (status.end) return error.EndOfStream;
157 }
158}
159
160/// Reads the stream until the end, ignoring all the data.
161/// Returns the number of bytes discarded.
162pub fn discardAll(br: *BufferedReader) anyerror!usize {
163 const list = &br.storage.buffer;
164 var total: usize = list.items.len;
165 list.items.len = 0;
166 total += try br.unbuffered_reader.discardAll();
167 return total;
168}
169
170/// Fill `buffer` with the next `buffer.len` bytes from the stream, advancing
171/// the seek position.
172///
173/// Invalidates previously returned values from `peek`.
174///
175/// If the provided buffer cannot be filled completely, `error.EndOfStream` is
176/// returned instead.
177///
178/// See also:
179/// * `peek`
180pub fn read(br: *BufferedReader, buffer: []u8) anyerror!void {
181 const list = &br.storage.buffer;
182 const in_buffer = list.items;
183 const seek = br.seek;
184 const proposed_seek = seek + in_buffer.len;
185 if (proposed_seek <= in_buffer.len) {
186 @memcpy(buffer, in_buffer[seek..proposed_seek]);
187 br.seek = proposed_seek;
188 return;
189 }
190 @memcpy(buffer[0..in_buffer.len], in_buffer);
191 list.items.len = 0;
192 br.seek = 0;
193 var i: usize = in_buffer.len;
194 while (true) {
195 const status = try br.unbuffered_reader.streamRead(&br.storage, .none);
196 const next_i = i + list.items.len;
197 if (next_i >= buffer.len) {
198 const remaining = buffer[i..];
199 @memcpy(remaining, list.items[0..remaining.len]);
200 br.seek = remaining.len;
201 return;
202 }
203 if (status.end) return error.EndOfStream;
204 @memcpy(buffer[i..next_i], list.items);
205 list.items.len = 0;
206 i = next_i;
207 }
208}
209
210/// Returns a slice of the next bytes of buffered data from the stream until
211/// `delimiter` is found, advancing the seek position.
212///
213/// Returned slice includes the delimiter as the last byte.
214///
215/// If the stream ends before the delimiter is found, `error.EndOfStream` is
216/// returned.
217///
218/// If the delimiter is not found within a number of bytes matching the
219/// capacity of the `BufferedReader`, `error.StreamTooLong` is returned.
220///
221/// Invalidates previously returned values from `peek`.
222///
223/// See also:
224/// * `takeDelimiterConclusive`
225/// * `peekDelimiterInclusive`
226pub fn takeDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
227 const result = try peekDelimiterInclusive(br, delimiter);
228 toss(result.len);
229 return result;
230}
231
232pub fn peekDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
233 const list = &br.storage.buffer;
234 const buffer = list.items;
235 const seek = br.seek;
236 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
237 @branchHint(.likely);
238 return buffer[seek .. end + 1];
239 }
240 const remainder = buffer[seek..];
241 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
242 var i = remainder.len;
243 list.items.len = i;
244 br.seek = 0;
245 while (i < list.capacity) {
246 const status = try br.unbuffered_reader.streamRead(&br.storage, .none);
247 if (std.mem.indexOfScalarPos(u8, list.items, i, delimiter)) |end| {
248 return list.items[0 .. end + 1];
249 }
250 if (status.end) return error.EndOfStream;
251 i = list.items.len;
252 }
253 return error.StreamTooLong;
254}
255
256/// Returns a slice of the next bytes of buffered data from the stream until
257/// `delimiter` is found, advancing the seek position.
258///
259/// Returned slice excludes the delimiter.
260///
261/// End-of-stream is treated equivalent to a delimiter.
262///
263/// If the delimiter is not found within a number of bytes matching the
264/// capacity of the `BufferedReader`, `error.StreamTooLong` is returned.
265///
266/// Invalidates previously returned values from `peek`.
267///
268/// See also:
269/// * `takeDelimiterInclusive`
270/// * `peekDelimiterConclusive`
271pub fn takeDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
272 const result = try peekDelimiterConclusive(br, delimiter);
273 toss(result.len);
274 return result;
275}
276
277pub fn peekDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror![]u8 {
278 const list = &br.storage.buffer;
279 const buffer = list.items;
280 const seek = br.seek;
281 if (std.mem.indexOfScalarPos(u8, buffer, seek, delimiter)) |end| {
282 @branchHint(.likely);
283 return buffer[seek..end];
284 }
285 const remainder = buffer[seek..];
286 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
287 var i = remainder.len;
288 list.items.len = i;
289 br.seek = 0;
290 while (i < list.capacity) {
291 const status = try br.unbuffered_reader.streamRead(&br.storage, .none);
292 if (std.mem.indexOfScalarPos(u8, list.items, i, delimiter)) |end| {
293 return list.items[0 .. end + 1];
294 }
295 if (status.end) return list.items;
296 i = list.items.len;
297 }
298 return error.StreamTooLong;
299}
300
301/// Appends to `bw` contents by reading from the stream until `delimiter` is found.
302/// Does not write the delimiter itself.
303///
304/// If stream ends before delimiter found, returns `error.EndOfStream`.
305///
306/// Returns number of bytes streamed.
307pub fn streamReadDelimiter(br: *BufferedReader, bw: *std.io.BufferedWriter, delimiter: u8) anyerror!usize {
308 _ = br;
309 _ = bw;
310 _ = delimiter;
311 @panic("TODO");
312}
313
314/// Appends to `bw` contents by reading from the stream until `delimiter` is found.
315/// Does not write the delimiter itself.
316///
317/// Succeeds if stream ends before delimiter found.
318///
319/// Returns number of bytes streamed as well as whether the input reached the end.
320/// The end is not signaled to the writer.
321pub fn streamReadDelimiterConclusive(
322 br: *BufferedReader,
323 bw: *std.io.BufferedWriter,
324 delimiter: u8,
325) anyerror!Reader.Status {
326 _ = br;
327 _ = bw;
328 _ = delimiter;
329 @panic("TODO");
330}
331
332/// Appends to `bw` contents by reading from the stream until `delimiter` is found.
333/// Does not write the delimiter itself.
334///
335/// If `limit` is exceeded, returns `error.StreamTooLong`.
336pub fn streamReadDelimiterLimited(
337 br: *BufferedReader,
338 bw: *BufferedWriter,
339 delimiter: u8,
340 limit: usize,
341) anyerror!void {
342 _ = br;
343 _ = bw;
344 _ = delimiter;
345 _ = limit;
346 @panic("TODO");
347}
348
349/// Reads from the stream until specified byte is found, discarding all data,
350/// including the delimiter.
351///
352/// If end of stream is found, this function succeeds.
353pub fn discardDelimiterConclusive(br: *BufferedReader, delimiter: u8) anyerror!void {
354 _ = br;
355 _ = delimiter;
356 @panic("TODO");
357}
358
359/// Reads from the stream until specified byte is found, discarding all data,
360/// excluding the delimiter.
361///
362/// If end of stream is found, `error.EndOfStream` is returned.
363pub fn discardDelimiterInclusive(br: *BufferedReader, delimiter: u8) anyerror!void {
364 _ = br;
365 _ = delimiter;
366 @panic("TODO");
367}
368
369/// Fills the buffer such that it contains at least `n` bytes, without
370/// advancing the seek position.
371///
372/// Returns `error.EndOfStream` if there are fewer than `n` bytes remaining.
373///
374/// Asserts buffer capacity is at least `n`.
375pub fn fill(br: *BufferedReader, n: usize) anyerror!void {
376 assert(n <= br.storage.buffer.capacity);
377 const list = &br.storage.buffer;
378 const buffer = list.items;
379 const seek = br.seek;
380 if (seek + n <= buffer.len) {
381 @branchHint(.likely);
382 return;
383 }
384 const remainder = buffer[seek..];
385 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
386 list.items.len = remainder.len;
387 br.seek = 0;
388 while (true) {
389 const status = try br.unbuffered_reader.streamRead(&br.storage, .none);
390 if (n <= list.items.len) return;
391 if (status.end) return error.EndOfStream;
392 }
393}
394
395/// Reads 1 byte from the stream or returns `error.EndOfStream`.
396pub fn takeByte(br: *BufferedReader) anyerror!u8 {
397 const buffer = br.storage.buffer.items;
398 const seek = br.seek;
399 if (seek >= buffer.len) {
400 @branchHint(.unlikely);
401 try fill(br, 1);
402 }
403 br.seek = seek + 1;
404 return buffer[seek];
405}
406
407/// Same as `readByte` except the returned byte is signed.
408pub fn takeByteSigned(br: *BufferedReader) anyerror!i8 {
409 return @bitCast(try br.readByte());
410}
411
412/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
413pub inline fn takeInt(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) anyerror!T {
414 const n = @divExact(@typeInfo(T).int.bits, 8);
415 return std.mem.readInt(T, try takeArray(br, n), endian);
416}
417
418/// Asserts the buffer was initialized with a capacity at least `n`.
419pub fn takeVarInt(br: *BufferedReader, comptime Int: type, endian: std.builtin.Endian, n: usize) anyerror!Int {
420 assert(n <= @sizeOf(Int));
421 return std.mem.readVarInt(Int, try take(br, n), endian);
422}
423
424/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
425pub fn takeStruct(br: *BufferedReader, comptime T: type) anyerror!*align(1) T {
426 // Only extern and packed structs have defined in-memory layout.
427 comptime assert(@typeInfo(T).@"struct".layout != .auto);
428 return @ptrCast(try takeArray(br, @sizeOf(T)));
429}
430
431/// Asserts the buffer was initialized with a capacity at least `@sizeOf(T)`.
432pub fn takeStructEndian(br: *BufferedReader, comptime T: type, endian: std.builtin.Endian) anyerror!T {
433 var res = (try br.takeStruct(T)).*;
434 if (native_endian != endian) std.mem.byteSwapAllFields(T, &res);
435 return res;
436}
437
438/// Reads an integer with the same size as the given enum's tag type. If the
439/// integer matches an enum tag, casts the integer to the enum tag and returns
440/// it. Otherwise, returns `error.InvalidEnumTag`.
441///
442/// Asserts the buffer was initialized with a capacity at least `@sizeOf(Enum)`.
443pub fn takeEnum(br: *BufferedReader, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum {
444 const Tag = @typeInfo(Enum).@"enum".tag_type;
445 const int = try takeInt(br, Tag, endian);
446 return std.meta.intToEnum(Enum, int);
447}
448
449test initFixed {
450 var br: BufferedReader = undefined;
451 br.initFixed("a\x02");
452 try testing.expect((try br.takeByte()) == 'a');
453 try testing.expect((try br.takeEnum(enum(u8) {
454 a = 0,
455 b = 99,
456 c = 2,
457 d = 3,
458 }, builtin.cpu.arch.endian())) == .c);
459 try testing.expectError(error.EndOfStream, br.takeByte());
460}
461
462test peek {
463 return error.Unimplemented;
464}
465
466test toss {
467 return error.Unimplemented;
468}
469
470test take {
471 return error.Unimplemented;
472}
473
474test takeArray {
475 return error.Unimplemented;
476}
477
478test discard {
479 var br: BufferedReader = undefined;
480 br.initFixed("foobar");
481 try br.discard(3);
482 try testing.expectEqualStrings("bar", try br.take(3));
483 try br.discard(0);
484 try testing.expectError(error.EndOfStream, br.discard(1));
485}
486
487test discardAll {
488 return error.Unimplemented;
489}
490
491test read {
492 return error.Unimplemented;
493}
494
495test takeDelimiterInclusive {
496 return error.Unimplemented;
497}
498
499test peekDelimiterInclusive {
500 return error.Unimplemented;
501}
502
503test takeDelimiterConclusive {
504 return error.Unimplemented;
505}
506
507test peekDelimiterConclusive {
508 return error.Unimplemented;
509}
510
511test streamReadDelimiter {
512 return error.Unimplemented;
513}
514
515test streamReadDelimiterConclusive {
516 return error.Unimplemented;
517}
518
519test streamReadDelimiterLimited {
520 return error.Unimplemented;
521}
522
523test discardDelimiterConclusive {
524 return error.Unimplemented;
525}
526
527test discardDelimiterInclusive {
528 return error.Unimplemented;
529}
530
531test fill {
532 return error.Unimplemented;
533}
534
535test takeByte {
536 return error.Unimplemented;
537}
538
539test takeByteSigned {
540 return error.Unimplemented;
541}
542
543test takeInt {
544 return error.Unimplemented;
545}
546
547test takeVarInt {
548 return error.Unimplemented;
549}
550
551test takeStruct {
552 return error.Unimplemented;
553}
554
555test takeStructEndian {
556 return error.Unimplemented;
557}
558
559test takeEnum {
560 return error.Unimplemented;
561}
lib/std/io/BufferedWriter.zig+103-41
......@@ -3,24 +3,37 @@ const BufferedWriter = @This();
33const assert = std.debug.assert;
44const native_endian = @import("builtin").target.cpu.arch.endian();
55const Writer = std.io.Writer;
6const Allocator = std.mem.Allocator;
67const testing = std.testing;
78
8/// Underlying stream to send bytes to.
9///
10/// A write will only be sent here if it could not fit into `buffer`, or if it
11/// is a `writeFile`.
12///
13/// `unbuffered_writer` may modify `buffer` if the number of bytes returned
14/// equals number of bytes provided. This property is exploited by
15/// `std.io.AllocatingWriter` for example.
16unbuffered_writer: Writer,
179/// User-provided storage that must outlive this `BufferedWriter`.
1810///
19/// If this has length zero, the writer is unbuffered, and `flush` is a no-op.
20buffer: []u8,
21/// Marks the end of `buffer` - before this are buffered bytes, after this is
22/// undefined.
23end: usize = 0,
11/// If this has capacity zero, the writer is unbuffered, and `flush` is a no-op.
12buffer: std.ArrayListUnmanaged(u8),
13mode: union(enum) {
14 /// Return `error.NoSpaceLeft` if a write could not fit into the buffer.
15 fixed,
16 /// Underlying stream to send bytes to.
17 ///
18 /// A write will only be sent here if it could not fit into `buffer`, or if
19 /// it is a `writeFile`.
20 ///
21 /// `unbuffered_writer` may modify `buffer` if the number of bytes returned
22 /// equals number of bytes provided. This property is exploited by
23 /// `std.io.AllocatingWriter` for example.
24 writer: Writer,
25 /// If this is provided, `buffer` will grow superlinearly rather than
26 /// become full.
27 allocator: Allocator,
28},
29
30pub fn deinit(bw: *BufferedWriter) void {
31 switch (bw.mode) {
32 .allocator => |gpa| bw.buffer.deinit(gpa),
33 .fixed, .writer => {},
34 }
35 bw.* = undefined;
36}
2437
2538/// Number of slices to store on the stack, when trying to send as many byte
2639/// vectors through the underlying write calls as possible.
......@@ -281,37 +294,44 @@ pub fn print(bw: *BufferedWriter, comptime format: []const u8, args: anytype) an
281294}
282295
283296pub fn writeByte(bw: *BufferedWriter, byte: u8) anyerror!void {
284 const buffer = bw.buffer;
285 const end = bw.end;
286 if (end == buffer.len) {
287 @branchHint(.unlikely);
288 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
289 while (true) {
290 const n = try bw.unbuffered_writer.writev(&buffers);
291 if (n == 0) {
292 @branchHint(.unlikely);
293 continue;
294 } else if (n >= buffer.len) {
295 @branchHint(.likely);
296 if (n > buffer.len) {
297 const list = &bw.buffer;
298 const buffer = list.items;
299 if (buffer.len < list.capacity) {
300 @branchHint(.likely);
301 buffer.ptr[buffer.len] = byte;
302 list.items.len = buffer.len + 1;
303 return;
304 }
305 switch (bw.mode) {
306 .fixed => return error.NoSpaceLeft,
307 .writer => |w| {
308 var buffers: [2][]const u8 = .{ buffer, &.{byte} };
309 while (true) {
310 const n = try w.writev(&buffers);
311 if (n == 0) {
312 @branchHint(.unlikely);
313 continue;
314 } else if (n >= buffer.len) {
297315 @branchHint(.likely);
298 bw.end = 0;
299 return;
300 } else {
301 buffer[0] = byte;
302 bw.end = 1;
303 return;
316 if (n > buffer.len) {
317 @branchHint(.likely);
318 list.items.len = 0;
319 return;
320 } else {
321 buffer[0] = byte;
322 list.items.len = 1;
323 return;
324 }
304325 }
326 const remainder = buffer[n..];
327 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
328 buffer[remainder.len] = byte;
329 list.items.len = remainder.len + 1;
330 return;
305331 }
306 const remainder = buffer[n..];
307 std.mem.copyForwards(u8, buffer[0..remainder.len], remainder);
308 buffer[remainder.len] = byte;
309 bw.end = remainder.len + 1;
310 return;
311 }
332 },
333 .allocator => |gpa| try list.append(gpa, byte),
312334 }
313 buffer[end] = byte;
314 bw.end = end + 1;
315335}
316336
317337/// Writes the same byte many times, performing the underlying write call as
......@@ -1553,3 +1573,45 @@ test "bytes.hex" {
15531573 const bytes_with_zeros = "\x00\x0E\xBA\xBE";
15541574 try std.testing.expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros});
15551575}
1576
1577test initFixed {
1578 {
1579 var buf: [255]u8 = undefined;
1580 var bw: BufferedWriter = undefined;
1581 bw.initFixed(&buf);
1582 try bw.print("{s}{s}!", .{ "Hello", "World" });
1583 try testing.expectEqualStrings("HelloWorld!", bw.getWritten());
1584 }
1585
1586 comptime {
1587 var buf: [255]u8 = undefined;
1588 var bw: BufferedWriter = undefined;
1589 bw.initFixed(&buf);
1590 try bw.print("{s}{s}!", .{ "Hello", "World" });
1591 try testing.expectEqualStrings("HelloWorld!", bw.getWritten());
1592 }
1593}
1594
1595test "fixed output" {
1596 var buffer: [10]u8 = undefined;
1597 var bw: BufferedWriter = undefined;
1598 bw.initFixed(&buffer);
1599
1600 try bw.writeAll("Hello");
1601 try testing.expect(std.mem.eql(u8, bw.getWritten(), "Hello"));
1602
1603 try bw.writeAll("world");
1604 try testing.expect(std.mem.eql(u8, bw.getWritten(), "Helloworld"));
1605
1606 try testing.expectError(error.NoSpaceLeft, bw.writeAll("!"));
1607 try testing.expect(std.mem.eql(u8, bw.getWritten(), "Helloworld"));
1608
1609 bw.reset();
1610 try testing.expect(bw.getWritten().len == 0);
1611
1612 try testing.expectError(error.NoSpaceLeft, bw.writeAll("Hello world!"));
1613 try testing.expect(std.mem.eql(u8, bw.getWritten(), "Hello worl"));
1614
1615 try bw.seekTo((try bw.getEndPos()) + 1);
1616 try testing.expectError(error.NoSpaceLeft, bw.writeAll("H"));
1617}
lib/std/io/CountingReader.zig created+29
......@@ -0,0 +1,29 @@
1//! A Reader that counts how many bytes has been read from it.
2
3const std = @import("../std.zig");
4const CountingReader = @This();
5
6child_reader: std.io.Reader,
7bytes_read: u64 = 0,
8
9pub fn read(self: *@This(), buf: []u8) anyerror!usize {
10 const amt = try self.child_reader.read(buf);
11 self.bytes_read += amt;
12 return amt;
13}
14
15pub fn reader(self: *@This()) std.io.Reader {
16 return .{ .context = self };
17}
18
19test CountingReader {
20 const bytes = "yay" ** 20;
21 var fbs: std.io.BufferedReader = undefined;
22 fbs.initFixed(bytes);
23 var counting_stream: CountingReader = .{ .child_reader = fbs.reader() };
24 var stream = counting_stream.reader().unbuffered();
25 while (stream.readByte()) |_| {} else |err| {
26 try std.testing.expectError(error.EndOfStream, err);
27 }
28 try std.testing.expect(counting_stream.bytes_read == bytes.len);
29}
lib/std/io/FixedBufferStream.zig deleted-148
......@@ -1,148 +0,0 @@
1//! This turns a const byte buffer into an `io.Reader`, or `io.SeekableStream`.
2
3const std = @import("../std.zig");
4const io = std.io;
5const testing = std.testing;
6const mem = std.mem;
7const assert = std.debug.assert;
8const FixedBufferStream = @This();
9
10buffer: []const u8,
11pos: usize = 0,
12
13pub const ReadError = error{};
14pub const SeekError = error{};
15pub const GetSeekPosError = error{};
16
17pub const Reader = io.Reader(*Self, ReadError, read);
18
19pub const SeekableStream = io.SeekableStream(
20 *Self,
21 SeekError,
22 GetSeekPosError,
23 seekTo,
24 seekBy,
25 getPos,
26 getEndPos,
27);
28
29const Self = @This();
30
31pub fn reader(self: *Self) Reader {
32 return .{ .context = self };
33}
34
35pub fn seekableStream(self: *Self) SeekableStream {
36 return .{ .context = self };
37}
38
39pub fn read(self: *Self, dest: []u8) ReadError!usize {
40 const size = @min(dest.len, self.buffer.len - self.pos);
41 const end = self.pos + size;
42
43 @memcpy(dest[0..size], self.buffer[self.pos..end]);
44 self.pos = end;
45
46 return size;
47}
48
49pub fn seekTo(self: *Self, pos: u64) SeekError!void {
50 self.pos = @min(std.math.lossyCast(usize, pos), self.buffer.len);
51}
52
53pub fn seekBy(self: *Self, amt: i64) SeekError!void {
54 if (amt < 0) {
55 const abs_amt = @abs(amt);
56 const abs_amt_usize = std.math.cast(usize, abs_amt) orelse std.math.maxInt(usize);
57 if (abs_amt_usize > self.pos) {
58 self.pos = 0;
59 } else {
60 self.pos -= abs_amt_usize;
61 }
62 } else {
63 const amt_usize = std.math.cast(usize, amt) orelse std.math.maxInt(usize);
64 const new_pos = std.math.add(usize, self.pos, amt_usize) catch std.math.maxInt(usize);
65 self.pos = @min(self.buffer.len, new_pos);
66 }
67}
68
69pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
70 return self.buffer.len;
71}
72
73pub fn getPos(self: *Self) GetSeekPosError!u64 {
74 return self.pos;
75}
76
77pub fn getWritten(self: Self) []const u8 {
78 return self.buffer[0..self.pos];
79}
80
81pub fn reset(self: *Self) void {
82 self.pos = 0;
83}
84
85test "output" {
86 var buf: [255]u8 = undefined;
87 var fbs: FixedBufferStream = .{ .buffer = &buf };
88 const stream = fbs.writer();
89
90 try stream.print("{s}{s}!", .{ "Hello", "World" });
91 try testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
92}
93
94test "output at comptime" {
95 comptime {
96 var buf: [255]u8 = undefined;
97 var fbs: FixedBufferStream = .{ .buffer = &buf };
98 const stream = fbs.writer();
99
100 try stream.print("{s}{s}!", .{ "Hello", "World" });
101 try testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten());
102 }
103}
104
105test "output 2" {
106 var buffer: [10]u8 = undefined;
107 var fbs: FixedBufferStream = .{ .buffer = &buffer };
108
109 try fbs.writer().writeAll("Hello");
110 try testing.expect(mem.eql(u8, fbs.getWritten(), "Hello"));
111
112 try fbs.writer().writeAll("world");
113 try testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
114
115 try testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("!"));
116 try testing.expect(mem.eql(u8, fbs.getWritten(), "Helloworld"));
117
118 fbs.reset();
119 try testing.expect(fbs.getWritten().len == 0);
120
121 try testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("Hello world!"));
122 try testing.expect(mem.eql(u8, fbs.getWritten(), "Hello worl"));
123
124 try fbs.seekTo((try fbs.getEndPos()) + 1);
125 try testing.expectError(error.NoSpaceLeft, fbs.writer().writeAll("H"));
126}
127
128test "input" {
129 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7 };
130 var fbs: FixedBufferStream = .{ .buffer = &bytes };
131
132 var dest: [4]u8 = undefined;
133
134 var amt_read = try fbs.reader().read(&dest);
135 try testing.expect(amt_read == 4);
136 try testing.expect(mem.eql(u8, dest[0..4], bytes[0..4]));
137
138 amt_read = try fbs.reader().read(&dest);
139 try testing.expect(amt_read == 3);
140 try testing.expect(mem.eql(u8, dest[0..3], bytes[4..7]));
141
142 amt_read = try fbs.reader().read(&dest);
143 try testing.expect(amt_read == 0);
144
145 try fbs.seekTo((try fbs.getEndPos()) + 1);
146 amt_read = try fbs.reader().read(&dest);
147 try testing.expect(amt_read == 0);
148}
lib/std/io/Reader.zig+138-257
......@@ -1,292 +1,173 @@
1context: *const anyopaque,
2readFn: *const fn (context: *const anyopaque, buffer: []u8) anyerror!usize,
1const std = @import("../std.zig");
2const Reader = @This();
3const assert = std.debug.assert;
34
4pub const Error = anyerror;
5context: *anyopaque,
6vtable: *const VTable,
7
8pub const VTable = struct {
9 /// Writes bytes starting from `offset` to `bw`, or returns
10 /// `error.Unseekable`, indicating `streamRead` should be used instead.
11 ///
12 /// Returns the number of bytes written, which will be at minimum `0` and at
13 /// most `limit`. The number of bytes read, including zero, does not
14 /// indicate end of stream.
15 ///
16 /// If the reader has an internal seek position, it is not mutated.
17 ///
18 /// The implementation should do a maximum of one underlying read call.
19 ///
20 /// If this is `null` it is equivalent to always returning
21 /// `error.Unseekable`.
22 seekRead: ?*const fn (ctx: *anyopaque, bw: *std.io.BufferedWriter, limit: Limit, offset: u64) anyerror!Status,
23
24 /// Writes bytes from the internally tracked stream position to `bw`, or
25 /// returns `error.Unstreamable`, indicating `seekRead` should be used
26 /// instead.
27 ///
28 /// Returns the number of bytes written, which will be at minimum `0` and at
29 /// most `limit`. The number of bytes read, including zero, does not
30 /// indicate end of stream.
31 ///
32 /// If the reader has an internal seek position, it moves forward in accordance
33 /// with the number of bytes return from this function.
34 ///
35 /// The implementation should do a maximum of one underlying read call.
36 ///
37 /// If this is `null` it is equivalent to always returning
38 /// `error.Unstreamable`.
39 streamRead: ?*const fn (ctx: *anyopaque, bw: *std.io.BufferedWriter, limit: Limit) anyerror!Status,
40};
541
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}
42pub const Len = @Type(.{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 });
1243
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}
44pub const Status = packed struct(usize) {
45 /// Number of bytes that were written to `writer`.
46 len: Len,
47 /// Indicates end of stream.
48 end: bool,
49};
1950
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}
51pub const Limit = enum(usize) {
52 none = std.math.maxInt(usize),
53 _,
54};
3555
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;
56/// Returns total number of bytes written to `w`.
57pub fn readAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {
58 if (r.vtable.pread != null) {
59 return seekReadAll(r, w) catch |err| switch (err) {
60 error.Unseekable => {},
61 else => return err,
62 };
63 }
64 return streamReadAll(r, w);
4065}
4166
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);
67/// Returns total number of bytes written to `w`.
68///
69/// May return `error.Unseekable`, indicating this function cannot be used to
70/// read from the reader.
71pub fn seekReadAll(r: Reader, w: *std.io.BufferedWriter, start_offset: u64) anyerror!usize {
72 const vtable_seekRead = r.vtable.seekRead.?;
73 var offset: u64 = start_offset;
74 while (true) {
75 const status = try vtable_seekRead(r.context, w, .none, offset);
76 offset += status.len;
77 if (status.end) return @intCast(offset - start_offset);
78 }
5379}
5480
55pub fn readAllArrayListAligned(
56 self: Self,
57 comptime alignment: ?Alignment,
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;
81/// Returns total number of bytes written to `w`.
82pub fn streamReadAll(r: Reader, w: *std.io.BufferedWriter) anyerror!usize {
83 const vtable_streamRead = r.vtable.streamRead.?;
84 var offset: usize = 0;
6485 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);
86 const status = try vtable_streamRead(r.context, w, .none);
87 offset += status.len;
88 if (status.end) return offset;
8289 }
8390}
8491
8592/// Allocates enough memory to hold all the contents of the stream. If the allocated
8693/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
94///
8795/// Caller owns returned memory.
96///
8897/// 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/// Appends to `bw` contents by reading from the stream until `delimiter` is found.
97/// Does not write the delimiter itself.
98/// If `optional_max_size` is not null and amount of written bytes exceeds `optional_max_size`,
99/// returns `error.StreamTooLong` and finishes appending.
100/// If `optional_max_size` is null, appending is unbounded.
101pub fn streamUntilDelimiter(
102 self: Self,
103 bw: *std.io.BufferedWriter,
104 delimiter: u8,
105 optional_max_size: ?usize,
106) anyerror!void {
107 if (optional_max_size) |max_size| {
108 for (0..max_size) |_| {
109 const byte: u8 = try self.readByte();
110 if (byte == delimiter) return;
111 try bw.writeByte(byte);
112 }
113 return error.StreamTooLong;
114 } else {
115 while (true) {
116 const byte: u8 = try self.readByte();
117 if (byte == delimiter) return;
118 try bw.writeByte(byte);
119 }
120 // Can not throw `error.StreamTooLong` since there are no boundary.
121 }
122}
123
124/// Reads from the stream until specified byte is found, discarding all data,
125/// including the delimiter.
126/// If end-of-stream is found, this function succeeds.
127pub fn skipUntilDelimiterOrEof(self: Self, delimiter: u8) anyerror!void {
128 while (true) {
129 const byte = self.readByte() catch |err| switch (err) {
130 error.EndOfStream => return,
131 else => |e| return e,
132 };
133 if (byte == delimiter) return;
134 }
135}
136
137/// Reads 1 byte from the stream or returns `error.EndOfStream`.
138pub fn readByte(self: Self) anyerror!u8 {
139 var result: [1]u8 = undefined;
140 const amt_read = try self.read(result[0..]);
141 if (amt_read < 1) return error.EndOfStream;
142 return result[0];
143}
144
145/// Same as `readByte` except the returned byte is signed.
146pub fn readByteSigned(self: Self) anyerror!i8 {
147 return @as(i8, @bitCast(try self.readByte()));
148}
98pub fn streamReadAlloc(r: Reader, gpa: std.mem.Allocator, max_size: usize) anyerror![]u8 {
99 const vtable_streamRead = r.vtable.streamRead.?;
149100
150/// Reads exactly `num_bytes` bytes and returns as an array.
151/// `num_bytes` must be comptime-known
152pub fn readBytesNoEof(self: Self, comptime num_bytes: usize) anyerror![num_bytes]u8 {
153 var bytes: [num_bytes]u8 = undefined;
154 try self.readNoEof(&bytes);
155 return bytes;
156}
157
158/// Reads bytes until `bounded.len` is equal to `num_bytes`,
159/// or the stream ends.
160///
161/// * it is assumed that `num_bytes` will not exceed `bounded.capacity()`
162pub fn readIntoBoundedBytes(
163 self: Self,
164 comptime num_bytes: usize,
165 bounded: *std.BoundedArray(u8, num_bytes),
166) anyerror!void {
167 while (bounded.len < num_bytes) {
168 // get at most the number of bytes free in the bounded array
169 const bytes_read = try self.read(bounded.unusedCapacitySlice());
170 if (bytes_read == 0) return;
101 var bw: std.io.BufferedWriter = .{
102 .buffer = .empty,
103 .mode = .{ .allocator = gpa },
104 };
105 const list = &bw.buffer;
106 defer list.deinit(gpa);
171107
172 // bytes_read will never be larger than @TypeOf(bounded.len)
173 // due to `self.read` being bounded by `bounded.unusedCapacitySlice()`
174 bounded.len += @as(@TypeOf(bounded.len), @intCast(bytes_read));
108 var remaining = max_size;
109 while (remaining > 0) {
110 const status = try vtable_streamRead(r.context, &bw, .init(remaining));
111 if (status.end) return list.toOwnedSlice(gpa);
112 remaining -= status.len;
175113 }
176114}
177115
178/// Reads at most `num_bytes` and returns as a bounded array.
179pub fn readBoundedBytes(self: Self, comptime num_bytes: usize) anyerror!std.BoundedArray(u8, num_bytes) {
180 var result = std.BoundedArray(u8, num_bytes){};
181 try self.readIntoBoundedBytes(num_bytes, &result);
182 return result;
116/// Reads the stream until the end, ignoring all the data.
117/// Returns the number of bytes discarded.
118pub fn discardAll(r: Reader) anyerror!usize {
119 var bw = std.io.null_writer.unbuffered();
120 return streamReadAll(r, &bw);
121}
122
123pub fn buffered(r: Reader, buffer: []u8) std.io.BufferedReader {
124 return .{
125 .reader = r,
126 .buffered_writer = .{
127 .buffer = buffer,
128 .mode = .fixed,
129 },
130 };
183131}
184132
185pub inline fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
186 const bytes = try self.readBytesNoEof(@divExact(@typeInfo(T).int.bits, 8));
187 return mem.readInt(T, &bytes, endian);
133pub fn allocating(r: Reader, gpa: std.mem.Allocator) std.io.BufferedReader {
134 return .{
135 .reader = r,
136 .buffered_writer = .{
137 .buffer = .empty,
138 .mode = .{ .allocator = gpa },
139 },
140 };
188141}
189142
190pub fn readVarInt(
191 self: Self,
192 comptime ReturnType: type,
193 endian: std.builtin.Endian,
194 size: usize,
195) anyerror!ReturnType {
196 assert(size <= @sizeOf(ReturnType));
197 var bytes_buf: [@sizeOf(ReturnType)]u8 = undefined;
198 const bytes = bytes_buf[0..size];
199 try self.readNoEof(bytes);
200 return mem.readVarInt(ReturnType, bytes, endian);
143pub fn unbuffered(r: Reader) std.io.BufferedReader {
144 return buffered(r, &.{});
201145}
202146
203/// Optional parameters for `skipBytes`
204pub const SkipBytesOptions = struct {
205 buf_size: usize = 512,
206};
207
208// `num_bytes` is a `u64` to match `off_t`
209/// Reads `num_bytes` bytes from the stream and discards them
210pub fn skipBytes(self: Self, num_bytes: u64, comptime options: SkipBytesOptions) anyerror!void {
211 var buf: [options.buf_size]u8 = undefined;
212 var remaining = num_bytes;
147test "when the backing reader provides one byte at a time" {
148 const OneByteReader = struct {
149 str: []const u8,
150 curr: usize,
213151
214 while (remaining > 0) {
215 const amt = @min(remaining, options.buf_size);
216 try self.readNoEof(buf[0..amt]);
217 remaining -= amt;
218 }
219}
152 fn read(self: *@This(), dest: []u8) anyerror!usize {
153 if (self.str.len <= self.curr or dest.len == 0)
154 return 0;
220155
221/// Reads `slice.len` bytes from the stream and returns if they are the same as the passed slice
222pub fn isBytes(self: Self, slice: []const u8) anyerror!bool {
223 var i: usize = 0;
224 var matches = true;
225 while (i < slice.len) : (i += 1) {
226 if (slice[i] != try self.readByte()) {
227 matches = false;
156 dest[0] = self.str[self.curr];
157 self.curr += 1;
158 return 1;
228159 }
229 }
230 return matches;
231}
232
233pub fn readStruct(self: Self, comptime T: type) anyerror!T {
234 // Only extern and packed structs have defined in-memory layout.
235 comptime assert(@typeInfo(T).@"struct".layout != .auto);
236 var res: [1]T = undefined;
237 try self.readNoEof(mem.sliceAsBytes(res[0..]));
238 return res[0];
239}
240160
241pub fn readStructEndian(self: Self, comptime T: type, endian: std.builtin.Endian) anyerror!T {
242 var res = try self.readStruct(T);
243 if (native_endian != endian) {
244 mem.byteSwapAllFields(T, &res);
245 }
246 return res;
247}
248
249/// Reads an integer with the same size as the given enum's tag type. If the integer matches
250/// an enum tag, casts the integer to the enum tag and returns it. Otherwise, returns an `error.InvalidValue`.
251/// TODO optimization taking advantage of most fields being in order
252pub fn readEnum(self: Self, comptime Enum: type, endian: std.builtin.Endian) anyerror!Enum {
253 const E = error{
254 /// An integer was read, but it did not match any of the tags in the supplied enum.
255 InvalidValue,
256 };
257 const type_info = @typeInfo(Enum).@"enum";
258 const tag = try self.readInt(type_info.tag_type, endian);
259
260 inline for (std.meta.fields(Enum)) |field| {
261 if (tag == field.value) {
262 return @field(Enum, field.name);
161 fn reader(self: *@This()) std.io.Reader {
162 return .{
163 .context = self,
164 };
263165 }
264 }
265
266 return E.InvalidValue;
267}
268
269/// Reads the stream until the end, ignoring all the data.
270/// Returns the number of bytes discarded.
271pub fn discard(self: Self) anyerror!u64 {
272 var trash: [4096]u8 = undefined;
273 var index: u64 = 0;
274 while (true) {
275 const n = try self.read(&trash);
276 if (n == 0) return index;
277 index += n;
278 }
279}
280
281const std = @import("../std.zig");
282const Self = @This();
283const math = std.math;
284const assert = std.debug.assert;
285const mem = std.mem;
286const testing = std.testing;
287const native_endian = @import("builtin").target.cpu.arch.endian();
288const Alignment = std.mem.Alignment;
166 };
289167
290test {
291 _ = @import("Reader/test.zig");
168 const str = "This is a test";
169 var one_byte_stream: OneByteReader = .init(str);
170 const res = try one_byte_stream.reader().streamReadAlloc(std.testing.allocator, str.len + 1);
171 defer std.testing.allocator.free(res);
172 try std.testing.expectEqualStrings(str, res);
292173}
lib/std/io/Reader/test.zig deleted-372
......@@ -1,372 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("../../std.zig");
3const testing = std.testing;
4
5test "Reader" {
6 var buf = "a\x02".*;
7 var fis = std.io.fixedBufferStream(&buf);
8 const reader = fis.reader();
9 try testing.expect((try reader.readByte()) == 'a');
10 try testing.expect((try reader.readEnum(enum(u8) {
11 a = 0,
12 b = 99,
13 c = 2,
14 d = 3,
15 }, builtin.cpu.arch.endian())) == .c);
16 try testing.expectError(error.EndOfStream, reader.readByte());
17}
18
19test "isBytes" {
20 var fis = std.io.fixedBufferStream("foobar");
21 const reader = fis.reader();
22 try testing.expectEqual(true, try reader.isBytes("foo"));
23 try testing.expectEqual(false, try reader.isBytes("qux"));
24}
25
26test "skipBytes" {
27 var fis = std.io.fixedBufferStream("foobar");
28 const reader = fis.reader();
29 try reader.skipBytes(3, .{});
30 try testing.expect(try reader.isBytes("bar"));
31 try reader.skipBytes(0, .{});
32 try testing.expectError(error.EndOfStream, reader.skipBytes(1, .{}));
33}
34
35test "readUntilDelimiterArrayList returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
36 const a = std.testing.allocator;
37 var list = std.ArrayList(u8).init(a);
38 defer list.deinit();
39
40 var fis = std.io.fixedBufferStream("0000\n1234\n");
41 const reader = fis.reader();
42
43 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
44 try std.testing.expectEqualStrings("0000", list.items);
45 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
46 try std.testing.expectEqualStrings("1234", list.items);
47 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
48}
49
50test "readUntilDelimiterArrayList returns an empty ArrayList" {
51 const a = std.testing.allocator;
52 var list = std.ArrayList(u8).init(a);
53 defer list.deinit();
54
55 var fis = std.io.fixedBufferStream("\n");
56 const reader = fis.reader();
57
58 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
59 try std.testing.expectEqualStrings("", list.items);
60}
61
62test "readUntilDelimiterArrayList returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
63 const a = std.testing.allocator;
64 var list = std.ArrayList(u8).init(a);
65 defer list.deinit();
66
67 var fis = std.io.fixedBufferStream("1234567\n");
68 const reader = fis.reader();
69
70 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterArrayList(&list, '\n', 5));
71 try std.testing.expectEqualStrings("12345", list.items);
72 try reader.readUntilDelimiterArrayList(&list, '\n', 5);
73 try std.testing.expectEqualStrings("67", list.items);
74}
75
76test "readUntilDelimiterArrayList returns EndOfStream" {
77 const a = std.testing.allocator;
78 var list = std.ArrayList(u8).init(a);
79 defer list.deinit();
80
81 var fis = std.io.fixedBufferStream("1234");
82 const reader = fis.reader();
83
84 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterArrayList(&list, '\n', 5));
85 try std.testing.expectEqualStrings("1234", list.items);
86}
87
88test "readUntilDelimiterAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
89 const a = std.testing.allocator;
90
91 var fis = std.io.fixedBufferStream("0000\n1234\n");
92 const reader = fis.reader();
93
94 {
95 const result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
96 defer a.free(result);
97 try std.testing.expectEqualStrings("0000", result);
98 }
99
100 {
101 const result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
102 defer a.free(result);
103 try std.testing.expectEqualStrings("1234", result);
104 }
105
106 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
107}
108
109test "readUntilDelimiterAlloc returns an empty ArrayList" {
110 const a = std.testing.allocator;
111
112 var fis = std.io.fixedBufferStream("\n");
113 const reader = fis.reader();
114
115 {
116 const result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
117 defer a.free(result);
118 try std.testing.expectEqualStrings("", result);
119 }
120}
121
122test "readUntilDelimiterAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
123 const a = std.testing.allocator;
124
125 var fis = std.io.fixedBufferStream("1234567\n");
126 const reader = fis.reader();
127
128 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterAlloc(a, '\n', 5));
129
130 const result = try reader.readUntilDelimiterAlloc(a, '\n', 5);
131 defer a.free(result);
132 try std.testing.expectEqualStrings("67", result);
133}
134
135test "readUntilDelimiterAlloc returns EndOfStream" {
136 const a = std.testing.allocator;
137
138 var fis = std.io.fixedBufferStream("1234");
139 const reader = fis.reader();
140
141 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiterAlloc(a, '\n', 5));
142}
143
144test "readUntilDelimiter returns bytes read until the delimiter" {
145 var buf: [5]u8 = undefined;
146 var fis = std.io.fixedBufferStream("0000\n1234\n");
147 const reader = fis.reader();
148 try std.testing.expectEqualStrings("0000", try reader.readUntilDelimiter(&buf, '\n'));
149 try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
150}
151
152test "readUntilDelimiter returns an empty string" {
153 var buf: [5]u8 = undefined;
154 var fis = std.io.fixedBufferStream("\n");
155 const reader = fis.reader();
156 try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
157}
158
159test "readUntilDelimiter returns StreamTooLong, then an empty string" {
160 var buf: [5]u8 = undefined;
161 var fis = std.io.fixedBufferStream("12345\n");
162 const reader = fis.reader();
163 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
164 try std.testing.expectEqualStrings("", try reader.readUntilDelimiter(&buf, '\n'));
165}
166
167test "readUntilDelimiter returns StreamTooLong, then bytes read until the delimiter" {
168 var buf: [5]u8 = undefined;
169 var fis = std.io.fixedBufferStream("1234567\n");
170 const reader = fis.reader();
171 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
172 try std.testing.expectEqualStrings("67", try reader.readUntilDelimiter(&buf, '\n'));
173}
174
175test "readUntilDelimiter returns EndOfStream" {
176 {
177 var buf: [5]u8 = undefined;
178 var fis = std.io.fixedBufferStream("");
179 const reader = fis.reader();
180 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
181 }
182 {
183 var buf: [5]u8 = undefined;
184 var fis = std.io.fixedBufferStream("1234");
185 const reader = fis.reader();
186 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
187 }
188}
189
190test "readUntilDelimiter returns bytes read until delimiter, then EndOfStream" {
191 var buf: [5]u8 = undefined;
192 var fis = std.io.fixedBufferStream("1234\n");
193 const reader = fis.reader();
194 try std.testing.expectEqualStrings("1234", try reader.readUntilDelimiter(&buf, '\n'));
195 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
196}
197
198test "readUntilDelimiter returns StreamTooLong, then EndOfStream" {
199 var buf: [5]u8 = undefined;
200 var fis = std.io.fixedBufferStream("12345");
201 const reader = fis.reader();
202 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
203 try std.testing.expectError(error.EndOfStream, reader.readUntilDelimiter(&buf, '\n'));
204}
205
206test "readUntilDelimiter writes all bytes read to the output buffer" {
207 var buf: [5]u8 = undefined;
208 var fis = std.io.fixedBufferStream("0000\n12345");
209 const reader = fis.reader();
210 _ = try reader.readUntilDelimiter(&buf, '\n');
211 try std.testing.expectEqualStrings("0000\n", &buf);
212 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiter(&buf, '\n'));
213 try std.testing.expectEqualStrings("12345", &buf);
214}
215
216test "readUntilDelimiterOrEofAlloc returns ArrayLists with bytes read until the delimiter, then EndOfStream" {
217 const a = std.testing.allocator;
218
219 var fis = std.io.fixedBufferStream("0000\n1234\n");
220 const reader = fis.reader();
221
222 {
223 const result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
224 defer a.free(result);
225 try std.testing.expectEqualStrings("0000", result);
226 }
227
228 {
229 const result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
230 defer a.free(result);
231 try std.testing.expectEqualStrings("1234", result);
232 }
233
234 try std.testing.expect((try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)) == null);
235}
236
237test "readUntilDelimiterOrEofAlloc returns an empty ArrayList" {
238 const a = std.testing.allocator;
239
240 var fis = std.io.fixedBufferStream("\n");
241 const reader = fis.reader();
242
243 {
244 const result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
245 defer a.free(result);
246 try std.testing.expectEqualStrings("", result);
247 }
248}
249
250test "readUntilDelimiterOrEofAlloc returns StreamTooLong, then an ArrayList with bytes read until the delimiter" {
251 const a = std.testing.allocator;
252
253 var fis = std.io.fixedBufferStream("1234567\n");
254 const reader = fis.reader();
255
256 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEofAlloc(a, '\n', 5));
257
258 const result = (try reader.readUntilDelimiterOrEofAlloc(a, '\n', 5)).?;
259 defer a.free(result);
260 try std.testing.expectEqualStrings("67", result);
261}
262
263test "readUntilDelimiterOrEof returns bytes read until the delimiter" {
264 var buf: [5]u8 = undefined;
265 var fis = std.io.fixedBufferStream("0000\n1234\n");
266 const reader = fis.reader();
267 try std.testing.expectEqualStrings("0000", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
268 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
269}
270
271test "readUntilDelimiterOrEof returns an empty string" {
272 var buf: [5]u8 = undefined;
273 var fis = std.io.fixedBufferStream("\n");
274 const reader = fis.reader();
275 try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
276}
277
278test "readUntilDelimiterOrEof returns StreamTooLong, then an empty string" {
279 var buf: [5]u8 = undefined;
280 var fis = std.io.fixedBufferStream("12345\n");
281 const reader = fis.reader();
282 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
283 try std.testing.expectEqualStrings("", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
284}
285
286test "readUntilDelimiterOrEof returns StreamTooLong, then bytes read until the delimiter" {
287 var buf: [5]u8 = undefined;
288 var fis = std.io.fixedBufferStream("1234567\n");
289 const reader = fis.reader();
290 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
291 try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
292}
293
294test "readUntilDelimiterOrEof returns null" {
295 var buf: [5]u8 = undefined;
296 var fis = std.io.fixedBufferStream("");
297 const reader = fis.reader();
298 try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
299}
300
301test "readUntilDelimiterOrEof returns bytes read until delimiter, then null" {
302 var buf: [5]u8 = undefined;
303 var fis = std.io.fixedBufferStream("1234\n");
304 const reader = fis.reader();
305 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
306 try std.testing.expect((try reader.readUntilDelimiterOrEof(&buf, '\n')) == null);
307}
308
309test "readUntilDelimiterOrEof returns bytes read until end-of-stream" {
310 var buf: [5]u8 = undefined;
311 var fis = std.io.fixedBufferStream("1234");
312 const reader = fis.reader();
313 try std.testing.expectEqualStrings("1234", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
314}
315
316test "readUntilDelimiterOrEof returns StreamTooLong, then bytes read until end-of-stream" {
317 var buf: [5]u8 = undefined;
318 var fis = std.io.fixedBufferStream("1234567");
319 const reader = fis.reader();
320 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
321 try std.testing.expectEqualStrings("67", (try reader.readUntilDelimiterOrEof(&buf, '\n')).?);
322}
323
324test "readUntilDelimiterOrEof writes all bytes read to the output buffer" {
325 var buf: [5]u8 = undefined;
326 var fis = std.io.fixedBufferStream("0000\n12345");
327 const reader = fis.reader();
328 _ = try reader.readUntilDelimiterOrEof(&buf, '\n');
329 try std.testing.expectEqualStrings("0000\n", &buf);
330 try std.testing.expectError(error.StreamTooLong, reader.readUntilDelimiterOrEof(&buf, '\n'));
331 try std.testing.expectEqualStrings("12345", &buf);
332}
333
334test "streamUntilDelimiter writes all bytes without delimiter to the output" {
335 const input_string = "some_string_with_delimiter!";
336 var input_fbs = std.io.fixedBufferStream(input_string);
337 const reader = input_fbs.reader();
338
339 var output: [input_string.len]u8 = undefined;
340 var output_fbs = std.io.fixedBufferStream(&output);
341 const writer = output_fbs.writer();
342
343 try reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len);
344 try std.testing.expectEqualStrings("some_string_with_delimiter", output_fbs.getWritten());
345 try std.testing.expectError(error.EndOfStream, reader.streamUntilDelimiter(writer, '!', input_fbs.buffer.len));
346
347 input_fbs.reset();
348 output_fbs.reset();
349
350 try std.testing.expectError(error.StreamTooLong, reader.streamUntilDelimiter(writer, '!', 5));
351}
352
353test "readBoundedBytes correctly reads into a new bounded array" {
354 const test_string = "abcdefg";
355 var fis = std.io.fixedBufferStream(test_string);
356 const reader = fis.reader();
357
358 var array = try reader.readBoundedBytes(10000);
359 try testing.expectEqualStrings(array.slice(), test_string);
360}
361
362test "readIntoBoundedBytes correctly reads into a provided bounded array" {
363 const test_string = "abcdefg";
364 var fis = std.io.fixedBufferStream(test_string);
365 const reader = fis.reader();
366
367 var bounded_array = std.BoundedArray(u8, 10000){};
368
369 // compile time error if the size is not the same at the provided `bounded.capacity()`
370 try reader.readIntoBoundedBytes(10000, &bounded_array);
371 try testing.expectEqualStrings(bounded_array.slice(), test_string);
372}
lib/std/io/Writer.zig+33-43
......@@ -17,7 +17,7 @@ pub const VTable = struct {
1717 /// Number of bytes returned may be zero, which does not mean
1818 /// end-of-stream. A subsequent call may return nonzero, or may signal end
1919 /// of stream via an error.
20 writeSplat: *const fn (context: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize,
20 writeSplat: *const fn (ctx: *anyopaque, data: []const []const u8, splat: usize) anyerror!usize,
2121
2222 /// Writes contents from an open file. `headers` are written first, then `len`
2323 /// bytes of `file` starting from `offset`, then `trailers`.
......@@ -29,9 +29,9 @@ pub const VTable = struct {
2929 /// end-of-stream. A subsequent call may return nonzero, or may signal end
3030 /// of stream via an error.
3131 writeFile: *const fn (
32 context: *anyopaque,
32 ctx: *anyopaque,
3333 file: std.fs.File,
34 offset: u64,
34 offset: Offset,
3535 /// When zero, it means copy until the end of the file is reached.
3636 len: FileLen,
3737 /// Headers and trailers must be passed together so that in case `len` is
......@@ -39,22 +39,33 @@ pub const VTable = struct {
3939 headers_and_trailers: []const []const u8,
4040 headers_len: usize,
4141 ) anyerror!usize,
42};
4243
43 pub const FileLen = enum(u64) {
44 zero = 0,
45 entire_file = std.math.maxInt(u64),
46 _,
44pub const Offset = enum(u64) {
45 none = std.math.maxInt(u64),
46 _,
4747
48 pub fn init(integer: u64) FileLen {
49 const result: FileLen = @enumFromInt(integer);
50 assert(result != .entire_file);
51 return result;
52 }
48 pub fn init(integer: u64) Offset {
49 const result: Offset = @enumFromInt(integer);
50 assert(result != .none);
51 return result;
52 }
53};
5354
54 pub fn int(len: FileLen) u64 {
55 return @intFromEnum(len);
56 }
57 };
55pub const FileLen = enum(u64) {
56 zero = 0,
57 entire_file = std.math.maxInt(u64),
58 _,
59
60 pub fn init(integer: u64) FileLen {
61 const result: FileLen = @enumFromInt(integer);
62 assert(result != .entire_file);
63 return result;
64 }
65
66 pub fn int(len: FileLen) u64 {
67 return @intFromEnum(len);
68 }
5869};
5970
6071pub fn writev(w: Writer, data: []const []const u8) anyerror!usize {
......@@ -93,34 +104,13 @@ pub fn unimplemented_writeFile(
93104 return error.Unimplemented;
94105}
95106
96pub fn write(w: Writer, bytes: []const u8) anyerror!usize {
97 const single: [1][]const u8 = .{bytes};
98 return w.vtable.writeSplat(w.context, &single, 1);
99}
100
101pub fn writeAll(w: Writer, bytes: []const u8) anyerror!void {
102 var index: usize = 0;
103 while (index < bytes.len) index += try w.vtable.writeSplat(w.context, &.{bytes[index..]}, 1);
104}
105
106/// The `data` parameter is mutable because this function needs to mutate the
107/// fields in order to handle partial writes from `VTable.writev`.
108pub fn writevAll(w: Writer, data: [][]const u8) anyerror!void {
109 var i: usize = 0;
110 while (true) {
111 var n = try w.vtable.writeSplat(w.context, data[i..], 1);
112 while (n >= data[i].len) {
113 n -= data[i].len;
114 i += 1;
115 if (i >= data.len) return;
116 }
117 data[i] = data[i][n..];
118 }
107pub fn buffered(w: Writer, buffer: []u8) std.io.BufferedWriter {
108 return .{
109 .buffer = .initBuffer(buffer),
110 .mode = .{ .writer = w },
111 };
119112}
120113
121114pub fn unbuffered(w: Writer) std.io.BufferedWriter {
122 return .{
123 .buffer = &.{},
124 .unbuffered_writer = w,
125 };
115 return buffered(w, &.{});
126116}
lib/std/io/buffered_reader.zig deleted-201
......@@ -1,201 +0,0 @@
1const std = @import("../std.zig");
2const io = std.io;
3const mem = std.mem;
4const assert = std.debug.assert;
5const testing = std.testing;
6
7pub fn BufferedReader(comptime buffer_size: usize, comptime ReaderType: type) type {
8 return struct {
9 unbuffered_reader: ReaderType,
10 buf: [buffer_size]u8 = undefined,
11 start: usize = 0,
12 end: usize = 0,
13
14 pub const Error = ReaderType.Error;
15 pub const Reader = io.Reader(*Self, Error, read);
16
17 const Self = @This();
18
19 pub fn read(self: *Self, dest: []u8) Error!usize {
20 // First try reading from the already buffered data onto the destination.
21 const current = self.buf[self.start..self.end];
22 if (current.len != 0) {
23 const to_transfer = @min(current.len, dest.len);
24 @memcpy(dest[0..to_transfer], current[0..to_transfer]);
25 self.start += to_transfer;
26 return to_transfer;
27 }
28
29 // If dest is large, read from the unbuffered reader directly into the destination.
30 if (dest.len >= buffer_size) {
31 return self.unbuffered_reader.read(dest);
32 }
33
34 // If dest is small, read from the unbuffered reader into our own internal buffer,
35 // and then transfer to destination.
36 self.end = try self.unbuffered_reader.read(&self.buf);
37 const to_transfer = @min(self.end, dest.len);
38 @memcpy(dest[0..to_transfer], self.buf[0..to_transfer]);
39 self.start = to_transfer;
40 return to_transfer;
41 }
42
43 pub fn reader(self: *Self) Reader {
44 return .{ .context = self };
45 }
46 };
47}
48
49pub fn bufferedReader(reader: anytype) BufferedReader(4096, @TypeOf(reader)) {
50 return .{ .unbuffered_reader = reader };
51}
52
53pub fn bufferedReaderSize(comptime size: usize, reader: anytype) BufferedReader(size, @TypeOf(reader)) {
54 return .{ .unbuffered_reader = reader };
55}
56
57test "OneByte" {
58 const OneByteReadReader = struct {
59 str: []const u8,
60 curr: usize,
61
62 const Error = error{NoError};
63 const Self = @This();
64 const Reader = io.Reader(*Self, Error, read);
65
66 fn init(str: []const u8) Self {
67 return Self{
68 .str = str,
69 .curr = 0,
70 };
71 }
72
73 fn read(self: *Self, dest: []u8) Error!usize {
74 if (self.str.len <= self.curr or dest.len == 0)
75 return 0;
76
77 dest[0] = self.str[self.curr];
78 self.curr += 1;
79 return 1;
80 }
81
82 fn reader(self: *Self) Reader {
83 return .{ .context = self };
84 }
85 };
86
87 const str = "This is a test";
88 var one_byte_stream = OneByteReadReader.init(str);
89 var buf_reader = bufferedReader(one_byte_stream.reader());
90 const stream = buf_reader.reader();
91
92 const res = try stream.readAllAlloc(testing.allocator, str.len + 1);
93 defer testing.allocator.free(res);
94 try testing.expectEqualSlices(u8, str, res);
95}
96
97fn smallBufferedReader(underlying_stream: anytype) BufferedReader(8, @TypeOf(underlying_stream)) {
98 return .{ .unbuffered_reader = underlying_stream };
99}
100test "Block" {
101 const BlockReader = struct {
102 block: []const u8,
103 reads_allowed: usize,
104 curr_read: usize,
105
106 const Error = error{NoError};
107 const Self = @This();
108 const Reader = io.Reader(*Self, Error, read);
109
110 fn init(block: []const u8, reads_allowed: usize) Self {
111 return Self{
112 .block = block,
113 .reads_allowed = reads_allowed,
114 .curr_read = 0,
115 };
116 }
117
118 fn read(self: *Self, dest: []u8) Error!usize {
119 if (self.curr_read >= self.reads_allowed) return 0;
120 @memcpy(dest[0..self.block.len], self.block);
121
122 self.curr_read += 1;
123 return self.block.len;
124 }
125
126 fn reader(self: *Self) Reader {
127 return .{ .context = self };
128 }
129 };
130
131 const block = "0123";
132
133 // len out == block
134 {
135 var test_buf_reader: BufferedReader(4, BlockReader) = .{
136 .unbuffered_reader = BlockReader.init(block, 2),
137 };
138 const reader = test_buf_reader.reader();
139 var out_buf: [4]u8 = undefined;
140 _ = try reader.readAll(&out_buf);
141 try testing.expectEqualSlices(u8, &out_buf, block);
142 _ = try reader.readAll(&out_buf);
143 try testing.expectEqualSlices(u8, &out_buf, block);
144 try testing.expectEqual(try reader.readAll(&out_buf), 0);
145 }
146
147 // len out < block
148 {
149 var test_buf_reader: BufferedReader(4, BlockReader) = .{
150 .unbuffered_reader = BlockReader.init(block, 2),
151 };
152 const reader = test_buf_reader.reader();
153 var out_buf: [3]u8 = undefined;
154 _ = try reader.readAll(&out_buf);
155 try testing.expectEqualSlices(u8, &out_buf, "012");
156 _ = try reader.readAll(&out_buf);
157 try testing.expectEqualSlices(u8, &out_buf, "301");
158 const n = try reader.readAll(&out_buf);
159 try testing.expectEqualSlices(u8, out_buf[0..n], "23");
160 try testing.expectEqual(try reader.readAll(&out_buf), 0);
161 }
162
163 // len out > block
164 {
165 var test_buf_reader: BufferedReader(4, BlockReader) = .{
166 .unbuffered_reader = BlockReader.init(block, 2),
167 };
168 const reader = test_buf_reader.reader();
169 var out_buf: [5]u8 = undefined;
170 _ = try reader.readAll(&out_buf);
171 try testing.expectEqualSlices(u8, &out_buf, "01230");
172 const n = try reader.readAll(&out_buf);
173 try testing.expectEqualSlices(u8, out_buf[0..n], "123");
174 try testing.expectEqual(try reader.readAll(&out_buf), 0);
175 }
176
177 // len out == 0
178 {
179 var test_buf_reader: BufferedReader(4, BlockReader) = .{
180 .unbuffered_reader = BlockReader.init(block, 2),
181 };
182 const reader = test_buf_reader.reader();
183 var out_buf: [0]u8 = undefined;
184 _ = try reader.readAll(&out_buf);
185 try testing.expectEqualSlices(u8, &out_buf, "");
186 }
187
188 // len bufreader buf > block
189 {
190 var test_buf_reader: BufferedReader(5, BlockReader) = .{
191 .unbuffered_reader = BlockReader.init(block, 2),
192 };
193 const reader = test_buf_reader.reader();
194 var out_buf: [4]u8 = undefined;
195 _ = try reader.readAll(&out_buf);
196 try testing.expectEqualSlices(u8, &out_buf, block);
197 _ = try reader.readAll(&out_buf);
198 try testing.expectEqualSlices(u8, &out_buf, block);
199 try testing.expectEqual(try reader.readAll(&out_buf), 0);
200 }
201}
lib/std/io/counting_reader.zig deleted-43
......@@ -1,43 +0,0 @@
1const std = @import("../std.zig");
2const io = std.io;
3const testing = std.testing;
4
5/// A Reader that counts how many bytes has been read from it.
6pub fn CountingReader(comptime ReaderType: anytype) type {
7 return struct {
8 child_reader: ReaderType,
9 bytes_read: u64 = 0,
10
11 pub const Error = ReaderType.Error;
12 pub const Reader = io.Reader(*@This(), Error, read);
13
14 pub fn read(self: *@This(), buf: []u8) Error!usize {
15 const amt = try self.child_reader.read(buf);
16 self.bytes_read += amt;
17 return amt;
18 }
19
20 pub fn reader(self: *@This()) Reader {
21 return .{ .context = self };
22 }
23 };
24}
25
26pub fn countingReader(reader: anytype) CountingReader(@TypeOf(reader)) {
27 return .{ .child_reader = reader };
28}
29
30test CountingReader {
31 const bytes = "yay" ** 100;
32 var fbs = io.fixedBufferStream(bytes);
33
34 var counting_stream = countingReader(fbs.reader());
35 const stream = counting_stream.reader();
36
37 //read and discard all bytes
38 while (stream.readByte()) |_| {} else |err| {
39 try testing.expect(err == error.EndOfStream);
40 }
41
42 try testing.expect(counting_stream.bytes_read == bytes.len);
43}
lib/std/io/seekable_stream.zig deleted-35
......@@ -1,35 +0,0 @@
1const std = @import("../std.zig");
2
3pub fn SeekableStream(
4 comptime Context: type,
5 comptime SeekErrorType: type,
6 comptime GetSeekPosErrorType: type,
7 comptime seekToFn: fn (context: Context, pos: u64) SeekErrorType!void,
8 comptime seekByFn: fn (context: Context, pos: i64) SeekErrorType!void,
9 comptime getPosFn: fn (context: Context) GetSeekPosErrorType!u64,
10 comptime getEndPosFn: fn (context: Context) GetSeekPosErrorType!u64,
11) type {
12 return struct {
13 context: Context,
14
15 const Self = @This();
16 pub const SeekError = SeekErrorType;
17 pub const GetSeekPosError = GetSeekPosErrorType;
18
19 pub fn seekTo(self: Self, pos: u64) SeekError!void {
20 return seekToFn(self.context, pos);
21 }
22
23 pub fn seekBy(self: Self, amt: i64) SeekError!void {
24 return seekByFn(self.context, amt);
25 }
26
27 pub fn getEndPos(self: Self) GetSeekPosError!u64 {
28 return getEndPosFn(self.context);
29 }
30
31 pub fn getPos(self: Self) GetSeekPosError!u64 {
32 return getPosFn(self.context);
33 }
34 };
35}
lib/std/io/stream_source.zig deleted-127
......@@ -1,127 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4
5/// Provides `io.Reader`, `io.Writer`, and `io.SeekableStream` for in-memory buffers as
6/// well as files.
7/// For memory sources, if the supplied byte buffer is const, then `io.Writer` is not available.
8/// The error set of the stream functions is the error set of the corresponding file functions.
9pub const StreamSource = union(enum) {
10 // TODO: expose UEFI files to std.os in a way that allows this to be true
11 const has_file = (builtin.os.tag != .freestanding and builtin.os.tag != .uefi);
12
13 /// The stream access is redirected to this buffer.
14 buffer: io.FixedBufferStream([]u8),
15
16 /// The stream access is redirected to this buffer.
17 /// Writing to the source will always yield `error.AccessDenied`.
18 const_buffer: io.FixedBufferStream([]const u8),
19
20 /// The stream access is redirected to this file.
21 /// On freestanding, this must never be initialized!
22 file: if (has_file) std.fs.File else void,
23
24 pub const ReadError = io.FixedBufferStream([]u8).ReadError || (if (has_file) std.fs.File.ReadError else error{});
25 pub const WriteError = error{AccessDenied} || io.FixedBufferStream([]u8).WriteError || (if (has_file) std.fs.File.WriteError else error{});
26 pub const SeekError = io.FixedBufferStream([]u8).SeekError || (if (has_file) std.fs.File.SeekError else error{});
27 pub const GetSeekPosError = io.FixedBufferStream([]u8).GetSeekPosError || (if (has_file) std.fs.File.GetSeekPosError else error{});
28
29 pub const Reader = io.Reader(*StreamSource, ReadError, read);
30 pub const Writer = io.Writer(*StreamSource, WriteError, write);
31 pub const SeekableStream = io.SeekableStream(
32 *StreamSource,
33 SeekError,
34 GetSeekPosError,
35 seekTo,
36 seekBy,
37 getPos,
38 getEndPos,
39 );
40
41 pub fn read(self: *StreamSource, dest: []u8) ReadError!usize {
42 switch (self.*) {
43 .buffer => |*x| return x.read(dest),
44 .const_buffer => |*x| return x.read(dest),
45 .file => |x| if (!has_file) unreachable else return x.read(dest),
46 }
47 }
48
49 pub fn write(self: *StreamSource, bytes: []const u8) WriteError!usize {
50 switch (self.*) {
51 .buffer => |*x| return x.write(bytes),
52 .const_buffer => return error.AccessDenied,
53 .file => |x| if (!has_file) unreachable else return x.write(bytes),
54 }
55 }
56
57 pub fn seekTo(self: *StreamSource, pos: u64) SeekError!void {
58 switch (self.*) {
59 .buffer => |*x| return x.seekTo(pos),
60 .const_buffer => |*x| return x.seekTo(pos),
61 .file => |x| if (!has_file) unreachable else return x.seekTo(pos),
62 }
63 }
64
65 pub fn seekBy(self: *StreamSource, amt: i64) SeekError!void {
66 switch (self.*) {
67 .buffer => |*x| return x.seekBy(amt),
68 .const_buffer => |*x| return x.seekBy(amt),
69 .file => |x| if (!has_file) unreachable else return x.seekBy(amt),
70 }
71 }
72
73 pub fn getEndPos(self: *StreamSource) GetSeekPosError!u64 {
74 switch (self.*) {
75 .buffer => |*x| return x.getEndPos(),
76 .const_buffer => |*x| return x.getEndPos(),
77 .file => |x| if (!has_file) unreachable else return x.getEndPos(),
78 }
79 }
80
81 pub fn getPos(self: *StreamSource) GetSeekPosError!u64 {
82 switch (self.*) {
83 .buffer => |*x| return x.getPos(),
84 .const_buffer => |*x| return x.getPos(),
85 .file => |x| if (!has_file) unreachable else return x.getPos(),
86 }
87 }
88
89 pub fn reader(self: *StreamSource) Reader {
90 return .{ .context = self };
91 }
92
93 pub fn writer(self: *StreamSource) Writer {
94 return .{ .context = self };
95 }
96
97 pub fn seekableStream(self: *StreamSource) SeekableStream {
98 return .{ .context = self };
99 }
100};
101
102test "refs" {
103 std.testing.refAllDecls(StreamSource);
104}
105
106test "mutable buffer" {
107 var buffer: [64]u8 = undefined;
108 var source = StreamSource{ .buffer = std.io.fixedBufferStream(&buffer) };
109
110 var writer = source.writer();
111
112 try writer.writeAll("Hello, World!");
113
114 try std.testing.expectEqualStrings("Hello, World!", source.buffer.getWritten());
115}
116
117test "const buffer" {
118 const buffer: [64]u8 = "Hello, World!".* ++ ([1]u8{0xAA} ** 51);
119 var source = StreamSource{ .const_buffer = std.io.fixedBufferStream(&buffer) };
120
121 var reader = source.reader();
122
123 var dst_buffer: [13]u8 = undefined;
124 try reader.readNoEof(&dst_buffer);
125
126 try std.testing.expectEqualStrings("Hello, World!", &dst_buffer);
127}
lib/std/net.zig+11-43
......@@ -1356,25 +1356,9 @@ fn linuxLookupNameFromHosts(
13561356 };
13571357 defer file.close();
13581358
1359 var buffered_reader = std.io.bufferedReader(file.reader());
1360 const reader = buffered_reader.reader();
1361 // TODO: rework buffered reader so that we can use its buffer directly when searching for delimiters
13621359 var line_buf: [512]u8 = undefined;
1363 var line_buf_writer: std.io.BufferedWriter = undefined;
1364 line_buf_writer.initFixed(&line_buf);
1365 while (true) {
1366 const line = if (reader.streamUntilDelimiter(&line_buf_writer, '\n', line_buf.len)) |_| l: {
1367 break :l line_buf_writer.getWritten();
1368 } else |err| switch (err) {
1369 error.EndOfStream => l: {
1370 if (line_buf_writer.getWritten().len == 0) break;
1371 // Skip to the delimiter in the reader, to fix parsing
1372 try reader.skipUntilDelimiterOrEof('\n');
1373 // Use the truncated line. A truncated comment or hostname will be handled correctly.
1374 break :l &line_buf;
1375 },
1376 else => |e| return e,
1377 };
1360 var br = file.reader().buffered(&line_buf);
1361 while (br.takeDelimiterConclusive('\n')) |line| {
13781362 var split_it = mem.splitScalar(u8, line, '#');
13791363 const no_comment_line = split_it.first();
13801364
......@@ -1406,7 +1390,7 @@ fn linuxLookupNameFromHosts(
14061390 canon.items.len = 0;
14071391 try canon.appendSlice(name_text);
14081392 }
1409 }
1393 } else |err| return err;
14101394}
14111395
14121396pub fn isValidHostName(hostname: []const u8) bool {
......@@ -1543,7 +1527,7 @@ const ResolvConf = struct {
15431527 }
15441528};
15451529
1546/// Ignores lines longer than 512 bytes.
1530/// Returns `error.StreamTooLong` if a line is longer than 512 bytes.
15471531/// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761
15481532fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
15491533 rc.* = ResolvConf{
......@@ -1564,30 +1548,14 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
15641548 };
15651549 defer file.close();
15661550
1567 var buf_reader = std.io.bufferedReader(file.reader());
1568 const stream = buf_reader.reader();
1569 // TODO: rework buffered reader so that we can use its buffer directly when searching for delimiters
15701551 var line_buf: [512]u8 = undefined;
1571 var line_buf_writer: std.io.BufferedWriter = undefined;
1572 line_buf_writer.initFixed(&line_buf);
1573 while (true) {
1574 const line = if (stream.streamUntilDelimiter(&line_buf_writer, '\n', line_buf.len)) |_| l: {
1575 break :l line_buf_writer.getWritten();
1576 } else |err| switch (err) {
1577 error.EndOfStream => l: {
1578 if (line_buf_writer.getWritten().len == 0) break;
1579 // Skip to the delimiter in the reader, to fix parsing
1580 try stream.skipUntilDelimiterOrEof('\n');
1581 // Give an empty line to the while loop, which will be skipped.
1582 break :l line_buf[0..0];
1583 },
1584 else => |e| return e,
1552 var br = file.reader().buffered(&line_buf);
1553 while (br.takeDelimiterConclusive('\n')) |line_with_comment| {
1554 const line = line: {
1555 var split = mem.splitScalar(u8, line_with_comment, '#');
1556 break :line split.first();
15851557 };
1586 const no_comment_line = no_comment_line: {
1587 var split = mem.splitScalar(u8, line, '#');
1588 break :no_comment_line split.first();
1589 };
1590 var line_it = mem.tokenizeAny(u8, no_comment_line, " \t");
1558 var line_it = mem.tokenizeAny(u8, line, " \t");
15911559
15921560 const token = line_it.next() orelse continue;
15931561 if (mem.eql(u8, token, "options")) {
......@@ -1615,7 +1583,7 @@ fn getResolvConf(allocator: mem.Allocator, rc: *ResolvConf) !void {
16151583 rc.search.items.len = 0;
16161584 try rc.search.appendSlice(line_it.rest());
16171585 }
1618 }
1586 } else |err| return err;
16191587
16201588 if (rc.ns.items.len == 0) {
16211589 return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53);