commit
74c80d2c7f
98
std/io.zig
98
std/io.zig
|
@ -331,6 +331,104 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a stream which supports 'un-reading' data, so that it can be read again.
|
||||||
|
/// This makes look-ahead style parsing much easier.
|
||||||
|
pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = this;
|
||||||
|
pub const Error = InStreamError;
|
||||||
|
pub const Stream = InStream(Error);
|
||||||
|
|
||||||
|
pub stream: Stream,
|
||||||
|
base: *Stream,
|
||||||
|
|
||||||
|
// Right now the look-ahead space is statically allocated, but a version with dynamic allocation
|
||||||
|
// is not too difficult to derive from this.
|
||||||
|
buffer: [buffer_size]u8,
|
||||||
|
index: usize,
|
||||||
|
at_end: bool,
|
||||||
|
|
||||||
|
pub fn init(base: *Stream) Self {
|
||||||
|
return Self{
|
||||||
|
.base = base,
|
||||||
|
.buffer = undefined,
|
||||||
|
.index = 0,
|
||||||
|
.at_end = false,
|
||||||
|
.stream = Stream{ .readFn = readFn },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putBackByte(self: *Self, byte: u8) void {
|
||||||
|
self.buffer[self.index] = byte;
|
||||||
|
self.index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn putBack(self: *Self, bytes: []const u8) void {
|
||||||
|
var pos = bytes.len;
|
||||||
|
while (pos != 0) {
|
||||||
|
pos -= 1;
|
||||||
|
self.putBackByte(bytes[pos]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
|
||||||
|
const self = @fieldParentPtr(Self, "stream", in_stream);
|
||||||
|
|
||||||
|
// copy over anything putBack()'d
|
||||||
|
var pos: usize = 0;
|
||||||
|
while (pos < dest.len and self.index != 0) {
|
||||||
|
dest[pos] = self.buffer[self.index - 1];
|
||||||
|
self.index -= 1;
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == dest.len or self.at_end) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ask the backing stream for more
|
||||||
|
const left = dest.len - pos;
|
||||||
|
const read = try self.base.read(dest[pos..]);
|
||||||
|
assert(read <= left);
|
||||||
|
|
||||||
|
self.at_end = (read < left);
|
||||||
|
return pos + read;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SliceStream = struct {
|
||||||
|
const Self = this;
|
||||||
|
pub const Error = error { };
|
||||||
|
pub const Stream = InStream(Error);
|
||||||
|
|
||||||
|
pub stream: Stream,
|
||||||
|
|
||||||
|
pos: usize,
|
||||||
|
slice: []const u8,
|
||||||
|
|
||||||
|
pub fn init(slice: []const u8) Self {
|
||||||
|
return Self{
|
||||||
|
.slice = slice,
|
||||||
|
.pos = 0,
|
||||||
|
.stream = Stream{ .readFn = readFn },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
|
||||||
|
const self = @fieldParentPtr(Self, "stream", in_stream);
|
||||||
|
const size = math.min(dest.len, self.slice.len - self.pos);
|
||||||
|
const end = self.pos + size;
|
||||||
|
|
||||||
|
mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
|
||||||
|
self.pos = end;
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
pub fn BufferedOutStream(comptime Error: type) type {
|
pub fn BufferedOutStream(comptime Error: type) type {
|
||||||
return BufferedOutStreamCustom(os.page_size, Error);
|
return BufferedOutStreamCustom(os.page_size, Error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,3 +60,54 @@ test "BufferOutStream" {
|
||||||
|
|
||||||
assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
|
assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "SliceStream" {
|
||||||
|
const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 };
|
||||||
|
var ss = io.SliceStream.init(bytes);
|
||||||
|
|
||||||
|
var dest: [4]u8 = undefined;
|
||||||
|
|
||||||
|
var read = try ss.stream.read(dest[0..4]);
|
||||||
|
assert(read == 4);
|
||||||
|
assert(mem.eql(u8, dest[0..4], bytes[0..4]));
|
||||||
|
|
||||||
|
read = try ss.stream.read(dest[0..4]);
|
||||||
|
assert(read == 3);
|
||||||
|
assert(mem.eql(u8, dest[0..3], bytes[4..7]));
|
||||||
|
|
||||||
|
read = try ss.stream.read(dest[0..4]);
|
||||||
|
assert(read == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "PeekStream" {
|
||||||
|
const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
|
var ss = io.SliceStream.init(bytes);
|
||||||
|
var ps = io.PeekStream(2, io.SliceStream.Error).init(&ss.stream);
|
||||||
|
|
||||||
|
var dest: [4]u8 = undefined;
|
||||||
|
|
||||||
|
ps.putBackByte(9);
|
||||||
|
ps.putBackByte(10);
|
||||||
|
|
||||||
|
var read = try ps.stream.read(dest[0..4]);
|
||||||
|
assert(read == 4);
|
||||||
|
assert(dest[0] == 10);
|
||||||
|
assert(dest[1] == 9);
|
||||||
|
assert(mem.eql(u8, dest[2..4], bytes[0..2]));
|
||||||
|
|
||||||
|
read = try ps.stream.read(dest[0..4]);
|
||||||
|
assert(read == 4);
|
||||||
|
assert(mem.eql(u8, dest[0..4], bytes[2..6]));
|
||||||
|
|
||||||
|
read = try ps.stream.read(dest[0..4]);
|
||||||
|
assert(read == 2);
|
||||||
|
assert(mem.eql(u8, dest[0..2], bytes[6..8]));
|
||||||
|
|
||||||
|
ps.putBackByte(11);
|
||||||
|
ps.putBackByte(12);
|
||||||
|
|
||||||
|
read = try ps.stream.read(dest[0..4]);
|
||||||
|
assert(read == 2);
|
||||||
|
assert(dest[0] == 12);
|
||||||
|
assert(dest[1] == 11);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue