| ... | @@ -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) |
| 121 | | 121 | |
| 122 | unbuffered_in_stream: *Stream, | 122 | unbuffered_in_stream: *Stream, |
| 123 | | 123 | |
| 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, | | |
| 127 | | 126 | |
| 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 | } |
| 143 | | 134 | |
| 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 nothing | 146 | // 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 stream | 150 | 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 | } |
| 187 | | 152 | 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" { |
| 235 | | 196 | |
| 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. |
| 238 | pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type { | 199 | pub 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, |
| 246 | | 207 | |
| 247 | // Right now the look-ahead space is statically allocated, but a version with dynamic allocation | 208 | 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 { |
| 252 | | 213 | 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 | }; |
| 262 | | 240 | |
| 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 | } |
| 267 | | 244 | |
| 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 | } |
| 275 | | 248 | |
| 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); |
| 278 | | 251 | |
| 279 | // copy over anything putBack()'d | 252 | // 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 | } | | |
| 290 | | 255 | |
| 291 | // ask the backing stream for more | 256 | // 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 |
| 607 | | 568 | |
| 608 | unbuffered_out_stream: *Stream, | 569 | unbuffered_out_stream: *Stream, |
| 609 | | 570 | |
| 610 | buffer: [buffer_size]u8, | 571 | const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size }); |
| 611 | index: usize, | 572 | fifo: FifoType, |
| 612 | | 573 | |
| 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 | } |
| 621 | | 581 | |
| 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 | } |
| 626 | | 590 | |
| 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); |
| 629 | | 593 | 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 | } |