| ... | ... | @@ -121,76 +121,37 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) |
| 121 | 121 | |
| 122 | 122 | unbuffered_in_stream: *Stream, |
| 123 | 123 | |
| 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, |
| 127 | 126 | |
| 128 | 127 | pub fn init(unbuffered_in_stream: *Stream) Self { |
| 129 | 128 | return Self{ |
| 130 | 129 | .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(), |
| 140 | 131 | .stream = Stream{ .readFn = readFn }, |
| 141 | 132 | }; |
| 142 | 133 | } |
| 143 | 134 | |
| 144 | 135 | fn readFn(in_stream: *Stream, dest: []u8) !usize { |
| 145 | 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 | 137 | 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) { |
| 165 | 146 | // reading from the unbuffered stream returned nothing |
| 166 | 147 | // so we have nothing left to read. |
| 167 | 148 | return dest_index; |
| 168 | 149 | } |
| 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); |
| 186 | 151 | } |
| 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; |
| 193 | 153 | } |
| 154 | return dest.len; |
| 194 | 155 | } |
| 195 | 156 | }; |
| 196 | 157 | } |
| ... | ... | @@ -235,7 +196,7 @@ test "io.BufferedInStream" { |
| 235 | 196 | |
| 236 | 197 | /// Creates a stream which supports 'un-reading' data, so that it can be read again. |
| 237 | 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 | 200 | return struct { |
| 240 | 201 | const Self = @This(); |
| 241 | 202 | pub const Error = InStreamError; |
| ... | ... | @@ -244,57 +205,57 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ |
| 244 | 205 | stream: Stream, |
| 245 | 206 | base: *Stream, |
| 246 | 207 | |
| 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 | }; |
| 262 | 240 | |
| 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}); |
| 266 | 243 | } |
| 267 | 244 | |
| 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); |
| 274 | 247 | } |
| 275 | 248 | |
| 276 | 249 | fn readFn(in_stream: *Stream, dest: []u8) Error!usize { |
| 277 | 250 | const self = @fieldParentPtr(Self, "stream", in_stream); |
| 278 | 251 | |
| 279 | 252 | // 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; |
| 290 | 255 | |
| 291 | 256 | // 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; |
| 298 | 259 | } |
| 299 | 260 | }; |
| 300 | 261 | } |
| ... | ... | @@ -607,52 +568,33 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr |
| 607 | 568 | |
| 608 | 569 | unbuffered_out_stream: *Stream, |
| 609 | 570 | |
| 610 | | buffer: [buffer_size]u8, |
| 611 | | index: usize, |
| 571 | const FifoType = std.fifo.LinearFifo(u8, std.fifo.LinearFifoBufferType{ .Static = buffer_size }); |
| 572 | fifo: FifoType, |
| 612 | 573 | |
| 613 | 574 | pub fn init(unbuffered_out_stream: *Stream) Self { |
| 614 | 575 | return Self{ |
| 615 | 576 | .unbuffered_out_stream = unbuffered_out_stream, |
| 616 | | .buffer = undefined, |
| 617 | | .index = 0, |
| 577 | .fifo = FifoType.init(), |
| 618 | 578 | .stream = Stream{ .writeFn = writeFn }, |
| 619 | 579 | }; |
| 620 | 580 | } |
| 621 | 581 | |
| 622 | 582 | 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 | } |
| 625 | 589 | } |
| 626 | 590 | |
| 627 | 591 | fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void { |
| 628 | 592 | 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()) { |
| 640 | 594 | try self.flush(); |
| 641 | 595 | return self.unbuffered_out_stream.write(bytes); |
| 642 | 596 | } |
| 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); |
| 656 | 598 | } |
| 657 | 599 | }; |
| 658 | 600 | } |