authorgravatar for nwsharp@live.comNathan Sharp <nwsharp@live.com> 2018-07-23 23:24:53-07:00
committergravatar for nwsharp@live.comNathan Sharp <nwsharp@live.com> 2018-07-23 23:30:40-07:00
log0046551852fe97dca2c295b84be3b0b4c3004e45
tree9d5f5a6a6b63ced63408af8d953d17a0ddd1bc8c
parent10bdf73a02c90dc375985e49b08b5020cfc20b93
signaturelock-open Commit is signed but in an unrecognized format.

std.io: PeekStream and SliceStream

SliceStream is a read-only stream wrapper around a slice of bytes. It allows adapting algorithms which work on InStreams to in-memory data. PeekStream is a stream wrapper which allows "putting back" bytes into the stream so that they can be read again. This will help make look-ahead parsers easier to write.

2 files changed, 149 insertions(+), 0 deletions(-)

std/io.zig+98
......@@ -331,6 +331,104 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
331331 };
332332}
333333
334/// Creates a stream which supports 'un-reading' data, so that it can be read again.
335/// This makes look-ahead style parsing much easier.
336pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type {
337 return struct {
338 const Self = this;
339 pub const Error = InStreamError;
340 pub const Stream = InStream(Error);
341
342 pub stream: Stream,
343 base: *Stream,
344
345 // Right now the look-ahead space is statically allocated, but a version with dynamic allocation
346 // is not too difficult to derive from this.
347 buffer: [buffer_size]u8,
348 index: usize,
349 at_end: bool,
350
351 pub fn init(base: *Stream) Self {
352 return Self{
353 .base = base,
354 .buffer = undefined,
355 .index = 0,
356 .at_end = false,
357 .stream = Stream{ .readFn = readFn },
358 };
359 }
360
361 pub fn putBackByte(self: *Self, byte: u8) void {
362 self.buffer[self.index] = byte;
363 self.index += 1;
364 }
365
366 pub fn putBack(self: *Self, bytes: []const u8) void {
367 var pos = bytes.len;
368 while (pos != 0) {
369 pos -= 1;
370 self.putBackByte(bytes[pos]);
371 }
372 }
373
374 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
375 const self = @fieldParentPtr(Self, "stream", in_stream);
376
377 // copy over anything putBack()'d
378 var pos: usize = 0;
379 while (pos < dest.len and self.index != 0) {
380 dest[pos] = self.buffer[self.index - 1];
381 self.index -= 1;
382 pos += 1;
383 }
384
385 if (pos == dest.len or self.at_end) {
386 return pos;
387 }
388
389 // ask the backing stream for more
390 const left = dest.len - pos;
391 const read = try self.base.read(dest[pos..]);
392 assert(read <= left);
393
394 self.at_end = (read < left);
395 return pos + read;
396 }
397
398 };
399}
400
401pub const SliceStream = struct {
402 const Self = this;
403 pub const Error = error { };
404 pub const Stream = InStream(Error);
405
406 pub stream: Stream,
407
408 pos: usize,
409 slice: []const u8,
410
411 pub fn init(slice: []const u8) Self {
412 return Self{
413 .slice = slice,
414 .pos = 0,
415 .stream = Stream{ .readFn = readFn },
416 };
417 }
418
419 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
420 const self = @fieldParentPtr(Self, "stream", in_stream);
421 const size = math.min(dest.len, self.slice.len - self.pos);
422 const end = self.pos + size;
423
424 mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
425 self.pos = end;
426
427 return size;
428 }
429
430};
431
334432pub fn BufferedOutStream(comptime Error: type) type {
335433 return BufferedOutStreamCustom(os.page_size, Error);
336434}
std/io_test.zig+51
......@@ -60,3 +60,54 @@ test "BufferOutStream" {
6060
6161 assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
6262}
63
64test "SliceStream" {
65 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 };
66 var ss = io.SliceStream.init(bytes);
67
68 var dest: [4]u8 = undefined;
69
70 var read = try ss.stream.read(dest[0..4]);
71 assert(read == 4);
72 assert(mem.eql(u8, dest[0..4], bytes[0..4]));
73
74 read = try ss.stream.read(dest[0..4]);
75 assert(read == 3);
76 assert(mem.eql(u8, dest[0..3], bytes[4..7]));
77
78 read = try ss.stream.read(dest[0..4]);
79 assert(read == 0);
80}
81
82test "PeekStream" {
83 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
84 var ss = io.SliceStream.init(bytes);
85 var ps = io.PeekStream(2, io.SliceStream.Error).init(&ss.stream);
86
87 var dest: [4]u8 = undefined;
88
89 ps.putBackByte(9);
90 ps.putBackByte(10);
91
92 var read = try ps.stream.read(dest[0..4]);
93 assert(read == 4);
94 assert(dest[0] == 10);
95 assert(dest[1] == 9);
96 assert(mem.eql(u8, dest[2..4], bytes[0..2]));
97
98 read = try ps.stream.read(dest[0..4]);
99 assert(read == 4);
100 assert(mem.eql(u8, dest[0..4], bytes[2..6]));
101
102 read = try ps.stream.read(dest[0..4]);
103 assert(read == 2);
104 assert(mem.eql(u8, dest[0..2], bytes[6..8]));
105
106 ps.putBackByte(11);
107 ps.putBackByte(12);
108
109 read = try ps.stream.read(dest[0..4]);
110 assert(read == 2);
111 assert(dest[0] == 12);
112 assert(dest[1] == 11);
113}