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)...@@ -121,76 +121,37 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
121121
122 unbuffered_in_stream: *Stream,122 unbuffered_in_stream: *Stream,
123123
124 buffer: [buffer_size]u8,124 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
125 start_index: usize,125 fifo: FifoType,
126 end_index: usize,
127126
128 pub fn init(unbuffered_in_stream: *Stream) Self {127 pub fn init(unbuffered_in_stream: *Stream) Self {
129 return Self{128 return Self{
130 .unbuffered_in_stream = unbuffered_in_stream,129 .unbuffered_in_stream = unbuffered_in_stream,
131 .buffer = undefined,130 .fifo = FifoType.init(),
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
140 .stream = Stream{ .readFn = readFn },131 .stream = Stream{ .readFn = readFn },
141 };132 };
142 }133 }
143134
144 fn readFn(in_stream: *Stream, dest: []u8) !usize {135 fn readFn(in_stream: *Stream, dest: []u8) !usize {
145 const self = @fieldParentPtr(Self, "stream", in_stream);136 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
154 var dest_index: usize = 0;137 var dest_index: usize = 0;
155 while (true) {138 while (dest_index < dest.len) {
156 const dest_space = dest.len - dest_index;139 const written = self.fifo.read(dest[dest_index..]);
157 if (dest_space == 0) {140 if (written == 0) {
158 return dest_index;141 // fifo empty, fill it
159 }142 const writable = self.fifo.writableSlice(0);
160 const amt_buffered = self.end_index - self.start_index;143 assert(writable.len > 0);
161 if (amt_buffered == 0) {144 const n = try self.unbuffered_in_stream.read(writable);
162 assert(self.end_index <= buffer_size);145 if (n == 0) {
163 // Make sure the last read actually gave us some data
164 if (self.end_index == 0) {
165 // reading from the unbuffered stream returned nothing146 // reading from the unbuffered stream returned nothing
166 // so we have nothing left to read.147 // so we have nothing left to read.
167 return dest_index;148 return dest_index;
168 }149 }
169 // we can read more data from the unbuffered stream150 self.fifo.update(n);
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 }
186 }151 }
187152 dest_index += written;
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;
193 }153 }
154 return dest.len;
194 }155 }
195 };156 };
196}157}
...@@ -235,7 +196,7 @@ test "io.BufferedInStream" {...@@ -235,7 +196,7 @@ test "io.BufferedInStream" {
235196
236/// Creates a stream which supports 'un-reading' data, so that it can be read again.197/// Creates a stream which supports 'un-reading' data, so that it can be read again.
237/// This makes look-ahead style parsing much easier.198/// 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 {
239 return struct {200 return struct {
240 const Self = @This();201 const Self = @This();
241 pub const Error = InStreamError;202 pub const Error = InStreamError;
...@@ -244,57 +205,57 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ...@@ -244,57 +205,57 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
244 stream: Stream,205 stream: Stream,
245 base: *Stream,206 base: *Stream,
246207
247 // Right now the look-ahead space is statically allocated, but a version with dynamic allocation208 const FifoType = std.fifo.LinearFifo(u8, buffer_type);
248 // is not too difficult to derive from this.209 fifo: FifoType,
249 buffer: [buffer_size]u8,210
250 index: usize,211 pub usingnamespace switch (buffer_type) {
251 at_end: bool,212 .Static => struct {
252213 pub fn init(base: *Stream) Self {
253 pub fn init(base: *Stream) Self {214 return .{
254 return Self{215 .base = base,
255 .base = base,216 .fifo = FifoType.init(),
256 .buffer = undefined,217 .stream = Stream{ .readFn = readFn },
257 .index = 0,218 };
258 .at_end = false,219 }
259 .stream = Stream{ .readFn = readFn },220 },
260 };221 .Slice => struct {
261 }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 {241 pub fn putBackByte(self: *Self, byte: u8) !void {
264 self.buffer[self.index] = byte;242 try self.putBack(&[_]u8{byte});
265 self.index += 1;
266 }243 }
267244
268 pub fn putBack(self: *Self, bytes: []const u8) void {245 pub fn putBack(self: *Self, bytes: []const u8) !void {
269 var pos = bytes.len;246 try self.fifo.unget(bytes);
270 while (pos != 0) {
271 pos -= 1;
272 self.putBackByte(bytes[pos]);
273 }
274 }247 }
275248
276 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {249 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
277 const self = @fieldParentPtr(Self, "stream", in_stream);250 const self = @fieldParentPtr(Self, "stream", in_stream);
278251
279 // copy over anything putBack()'d252 // copy over anything putBack()'d
280 var pos: usize = 0;253 var dest_index = self.fifo.read(dest);
281 while (pos < dest.len and self.index != 0) {254 if (dest_index == dest.len) return dest_index;
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 }
290255
291 // ask the backing stream for more256 // ask the backing stream for more
292 const left = dest.len - pos;257 dest_index += try self.base.read(dest[dest_index..]);
293 const read = try self.base.read(dest[pos..]);258 return dest_index;
294 assert(read <= left);
295
296 self.at_end = (read < left);
297 return pos + read;
298 }259 }
299 };260 };
300}261}
...@@ -607,52 +568,33 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr...@@ -607,52 +568,33 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr
607568
608 unbuffered_out_stream: *Stream,569 unbuffered_out_stream: *Stream,
609570
610 buffer: [buffer_size]u8,571 const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size });
611 index: usize,572 fifo: FifoType,
612573
613 pub fn init(unbuffered_out_stream: *Stream) Self {574 pub fn init(unbuffered_out_stream: *Stream) Self {
614 return Self{575 return Self{
615 .unbuffered_out_stream = unbuffered_out_stream,576 .unbuffered_out_stream = unbuffered_out_stream,
616 .buffer = undefined,577 .fifo = FifoType.init(),
617 .index = 0,
618 .stream = Stream{ .writeFn = writeFn },578 .stream = Stream{ .writeFn = writeFn },
619 };579 };
620 }580 }
621581
622 pub fn flush(self: *Self) !void {582 pub fn flush(self: *Self) !void {
623 try self.unbuffered_out_stream.write(self.buffer[0..self.index]);583 while (true) {
624 self.index = 0;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 }
625 }589 }
626590
627 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {591 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
628 const self = @fieldParentPtr(Self, "stream", out_stream);592 const self = @fieldParentPtr(Self, "stream", out_stream);
629593 if (bytes.len >= self.fifo.writableLength()) {
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) {
640 try self.flush();594 try self.flush();
641 return self.unbuffered_out_stream.write(bytes);595 return self.unbuffered_out_stream.write(bytes);
642 }596 }
643 var src_index: usize = 0;597 self.fifo.writeAssumeCapacity(bytes);
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 }
656 }598 }
657 };599 };
658}600}
lib/std/io/test.zig+8-7
...@@ -5,6 +5,7 @@ const meta = std.meta;...@@ -5,6 +5,7 @@ const meta = std.meta;
5const trait = std.trait;5const trait = std.trait;
6const DefaultPrng = std.rand.DefaultPrng;6const DefaultPrng = std.rand.DefaultPrng;
7const expect = std.testing.expect;7const expect = std.testing.expect;
8const expectEqual = std.testing.expectEqual;
8const expectError = std.testing.expectError;9const expectError = std.testing.expectError;
9const mem = std.mem;10const mem = std.mem;
10const fs = std.fs;11const fs = std.fs;
...@@ -44,8 +45,8 @@ test "write a file, read it, then delete it" {...@@ -44,8 +45,8 @@ test "write a file, read it, then delete it" {
44 defer file.close();45 defer file.close();
4546
46 const file_size = try file.getEndPos();47 const file_size = try file.getEndPos();
47 const expected_file_size = "begin".len + data.len + "end".len;48 const expected_file_size: u64 = "begin".len + data.len + "end".len;
48 expect(file_size == expected_file_size);49 expectEqual(expected_file_size, file_size);
4950
50 var file_in_stream = file.inStream();51 var file_in_stream = file.inStream();
51 var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream);52 var buf_stream = io.BufferedInStream(File.ReadError).init(&file_in_stream.stream);
...@@ -93,12 +94,12 @@ test "SliceInStream" {...@@ -93,12 +94,12 @@ test "SliceInStream" {
93test "PeekStream" {94test "PeekStream" {
94 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };95 const bytes = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
95 var ss = io.SliceInStream.init(&bytes);96 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
98 var dest: [4]u8 = undefined;99 var dest: [4]u8 = undefined;
99100
100 ps.putBackByte(9);101 try ps.putBackByte(9);
101 ps.putBackByte(10);102 try ps.putBackByte(10);
102103
103 var read = try ps.stream.read(dest[0..4]);104 var read = try ps.stream.read(dest[0..4]);
104 expect(read == 4);105 expect(read == 4);
...@@ -114,8 +115,8 @@ test "PeekStream" {...@@ -114,8 +115,8 @@ test "PeekStream" {
114 expect(read == 2);115 expect(read == 2);
115 expect(mem.eql(u8, dest[0..2], bytes[6..8]));116 expect(mem.eql(u8, dest[0..2], bytes[6..8]));
116117
117 ps.putBackByte(11);118 try ps.putBackByte(11);
118 ps.putBackByte(12);119 try ps.putBackByte(12);
119120
120 read = try ps.stream.read(dest[0..4]);121 read = try ps.stream.read(dest[0..4]);
121 expect(read == 2);122 expect(read == 2);