authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-18 16:48:58-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-18 16:48:58-05:00
log7f92d0d4a44258a661a5dabf9800210f5ee31484
tree08b33e396c8fc816ea9c7d41348514d0342d82e3
parent662996e4a8826f62f76bdafdb7ccbb1d52210c96
parenta8d7652001525881fe17ddf42f5f86671d706b4b
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'daurnimator-use-fifo-from-stdio'

closes #3763

2 files changed, 74 insertions(+), 131 deletions(-)

lib/std/io.zig+66-124
......@@ -121,76 +121,37 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
121121
122122 unbuffered_in_stream: *Stream,
123123
124 buffer: [buffer_size]u8,
125 start_index: usize,
126 end_index: usize,
124 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
125 fifo: FifoType,
127126
128127 pub fn init(unbuffered_in_stream: *Stream) Self {
129128 return Self{
130129 .unbuffered_in_stream = unbuffered_in_stream,
131 .buffer = undefined,
132
133 // Initialize these two fields to buffer_size so that
134 // in `readFn` we treat the state as being able to read
135 // more from the unbuffered stream. If we set them to 0
136 // and 0, the code would think we already hit EOF.
137 .start_index = buffer_size,
138 .end_index = buffer_size,
139
130 .fifo = FifoType.init(),
140131 .stream = Stream{ .readFn = readFn },
141132 };
142133 }
143134
144135 fn readFn(in_stream: *Stream, dest: []u8) !usize {
145136 const self = @fieldParentPtr(Self, "stream", in_stream);
146
147 // Hot path for one byte reads
148 if (dest.len == 1 and self.end_index > self.start_index) {
149 dest[0] = self.buffer[self.start_index];
150 self.start_index += 1;
151 return 1;
152 }
153
154137 var dest_index: usize = 0;
155 while (true) {
156 const dest_space = dest.len - dest_index;
157 if (dest_space == 0) {
158 return dest_index;
159 }
160 const amt_buffered = self.end_index - self.start_index;
161 if (amt_buffered == 0) {
162 assert(self.end_index <= buffer_size);
163 // Make sure the last read actually gave us some data
164 if (self.end_index == 0) {
138 while (dest_index < dest.len) {
139 const written = self.fifo.read(dest[dest_index..]);
140 if (written == 0) {
141 // fifo empty, fill it
142 const writable = self.fifo.writableSlice(0);
143 assert(writable.len > 0);
144 const n = try self.unbuffered_in_stream.read(writable);
145 if (n == 0) {
165146 // reading from the unbuffered stream returned nothing
166147 // so we have nothing left to read.
167148 return dest_index;
168149 }
169 // we can read more data from the unbuffered stream
170 if (dest_space < buffer_size) {
171 self.start_index = 0;
172 self.end_index = try self.unbuffered_in_stream.read(self.buffer[0..]);
173
174 // Shortcut
175 if (self.end_index >= dest_space) {
176 mem.copy(u8, dest[dest_index..], self.buffer[0..dest_space]);
177 self.start_index = dest_space;
178 return dest.len;
179 }
180 } else {
181 // asking for so much data that buffering is actually less efficient.
182 // forward the request directly to the unbuffered stream
183 const amt_read = try self.unbuffered_in_stream.read(dest[dest_index..]);
184 return dest_index + amt_read;
185 }
150 self.fifo.update(n);
186151 }
187
188 const copy_amount = math.min(dest_space, amt_buffered);
189 const copy_end_index = self.start_index + copy_amount;
190 mem.copy(u8, dest[dest_index..], self.buffer[self.start_index..copy_end_index]);
191 self.start_index = copy_end_index;
192 dest_index += copy_amount;
152 dest_index += written;
193153 }
154 return dest.len;
194155 }
195156 };
196157}
......@@ -235,7 +196,7 @@ test "io.BufferedInStream" {
235196
236197/// Creates a stream which supports 'un-reading' data, so that it can be read again.
237198/// This makes look-ahead style parsing much easier.
238pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type {
199pub fn PeekStream(comptime buffer_type: std.fifo.LinearFifoBufferType, comptime InStreamError: type) type {
239200 return struct {
240201 const Self = @This();
241202 pub const Error = InStreamError;
......@@ -244,57 +205,57 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
244205 stream: Stream,
245206 base: *Stream,
246207
247 // Right now the look-ahead space is statically allocated, but a version with dynamic allocation
248 // is not too difficult to derive from this.
249 buffer: [buffer_size]u8,
250 index: usize,
251 at_end: bool,
252
253 pub fn init(base: *Stream) Self {
254 return Self{
255 .base = base,
256 .buffer = undefined,
257 .index = 0,
258 .at_end = false,
259 .stream = Stream{ .readFn = readFn },
260 };
261 }
208 const FifoType = std.fifo.LinearFifo(u8, buffer_type);
209 fifo: FifoType,
210
211 pub usingnamespace switch (buffer_type) {
212 .Static => struct {
213 pub fn init(base: *Stream) Self {
214 return .{
215 .base = base,
216 .fifo = FifoType.init(),
217 .stream = Stream{ .readFn = readFn },
218 };
219 }
220 },
221 .Slice => struct {
222 pub fn init(base: *Stream, buf: []u8) Self {
223 return .{
224 .base = base,
225 .fifo = FifoType.init(buf),
226 .stream = Stream{ .readFn = readFn },
227 };
228 }
229 },
230 .Dynamic => struct {
231 pub fn init(base: *Stream, allocator: *mem.Allocator) Self {
232 return .{
233 .base = base,
234 .fifo = FifoType.init(allocator),
235 .stream = Stream{ .readFn = readFn },
236 };
237 }
238 },
239 };
262240
263 pub fn putBackByte(self: *Self, byte: u8) void {
264 self.buffer[self.index] = byte;
265 self.index += 1;
241 pub fn putBackByte(self: *Self, byte: u8) !void {
242 try self.putBack(&[_]u8{byte});
266243 }
267244
268 pub fn putBack(self: *Self, bytes: []const u8) void {
269 var pos = bytes.len;
270 while (pos != 0) {
271 pos -= 1;
272 self.putBackByte(bytes[pos]);
273 }
245 pub fn putBack(self: *Self, bytes: []const u8) !void {
246 try self.fifo.unget(bytes);
274247 }
275248
276249 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
277250 const self = @fieldParentPtr(Self, "stream", in_stream);
278251
279252 // copy over anything putBack()'d
280 var pos: usize = 0;
281 while (pos < dest.len and self.index != 0) {
282 dest[pos] = self.buffer[self.index - 1];
283 self.index -= 1;
284 pos += 1;
285 }
286
287 if (pos == dest.len or self.at_end) {
288 return pos;
289 }
253 var dest_index = self.fifo.read(dest);
254 if (dest_index == dest.len) return dest_index;
290255
291256 // ask the backing stream for more
292 const left = dest.len - pos;
293 const read = try self.base.read(dest[pos..]);
294 assert(read <= left);
295
296 self.at_end = (read < left);
297 return pos + read;
257 dest_index += try self.base.read(dest[dest_index..]);
258 return dest_index;
298259 }
299260 };
300261}
......@@ -607,52 +568,33 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr
607568
608569 unbuffered_out_stream: *Stream,
609570
610 buffer: [buffer_size]u8,
611 index: usize,
571 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
572 fifo: FifoType,
612573
613574 pub fn init(unbuffered_out_stream: *Stream) Self {
614575 return Self{
615576 .unbuffered_out_stream = unbuffered_out_stream,
616 .buffer = undefined,
617 .index = 0,
577 .fifo = FifoType.init(),
618578 .stream = Stream{ .writeFn = writeFn },
619579 };
620580 }
621581
622582 pub fn flush(self: *Self) !void {
623 try self.unbuffered_out_stream.write(self.buffer[0..self.index]);
624 self.index = 0;
583 while (true) {
584 const slice = self.fifo.readableSlice(0);
585 if (slice.len == 0) break;
586 try self.unbuffered_out_stream.write(slice);
587 self.fifo.discard(slice.len);
588 }
625589 }
626590
627591 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
628592 const self = @fieldParentPtr(Self, "stream", out_stream);
629
630 if (bytes.len == 1) {
631 // This is not required logic but a shorter path
632 // for single byte writes
633 self.buffer[self.index] = bytes[0];
634 self.index += 1;
635 if (self.index == buffer_size) {
636 try self.flush();
637 }
638 return;
639 } else if (bytes.len >= self.buffer.len) {
593 if (bytes.len >= self.fifo.writableLength()) {
640594 try self.flush();
641595 return self.unbuffered_out_stream.write(bytes);
642596 }
643 var src_index: usize = 0;
644
645 while (src_index < bytes.len) {
646 const dest_space_left = self.buffer.len - self.index;
647 const copy_amt = math.min(dest_space_left, bytes.len - src_index);
648 mem.copy(u8, self.buffer[self.index..], bytes[src_index .. src_index + copy_amt]);
649 self.index += copy_amt;
650 assert(self.index <= self.buffer.len);
651 if (self.index == self.buffer.len) {
652 try self.flush();
653 }
654 src_index += copy_amt;
655 }
597 self.fifo.writeAssumeCapacity(bytes);
656598 }
657599 };
658600}
lib/std/io/test.zig+8-7
......@@ -5,6 +5,7 @@ const meta = std.meta;
55const trait = std.trait;
66const DefaultPrng = std.rand.DefaultPrng;
77const expect = std.testing.expect;
8const expectEqual = std.testing.expectEqual;
89const expectError = std.testing.expectError;
910const mem = std.mem;
1011const fs = std.fs;
......@@ -44,8 +45,8 @@ test "write a file, read it, then delete it" {
4445 defer file.close();
4546
4647 const file_size = try file.getEndPos();
47 const expected_file_size = "begin".len + data.len + "end".len;
48 expect(file_size == expected_file_size);
48 const expected_file_size: u64 = "begin".len + data.len + "end".len;
49 expectEqual(expected_file_size, file_size);
4950
5051 var file_in_stream = file.inStream();
5152 var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream);
......@@ -93,12 +94,12 @@ test "SliceInStream" {
9394test "PeekStream" {
9495 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
9596 var ss = io.SliceInStream.init(&bytes);
96 var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream);
97 var ps = io.PeekStream(.{ .Static = 2 }, io.SliceInStream.Error).init(&ss.stream);
9798
9899 var dest: [4]u8 = undefined;
99100
100 ps.putBackByte(9);
101 ps.putBackByte(10);
101 try ps.putBackByte(9);
102 try ps.putBackByte(10);
102103
103104 var read = try ps.stream.read(dest[0..4]);
104105 expect(read == 4);
......@@ -114,8 +115,8 @@ test "PeekStream" {
114115 expect(read == 2);
115116 expect(mem.eql(u8, dest[0..2], bytes[6..8]));
116117
117 ps.putBackByte(11);
118 ps.putBackByte(12);
118 try ps.putBackByte(11);
119 try ps.putBackByte(12);
119120
120121 read = try ps.stream.read(dest[0..4]);
121122 expect(read == 2);