mirror of https://github.com/Wilfred/difftastic/
193 lines
5.5 KiB
Plaintext
193 lines
5.5 KiB
Plaintext
use bytes;
|
|
use errors;
|
|
use io;
|
|
|
|
type dynamic_stream = struct {
|
|
stream: io::stream,
|
|
buf: []u8,
|
|
pos: size,
|
|
};
|
|
|
|
// Creates an [io::stream] which dynamically allocates a buffer to store writes
|
|
// into. Subsequent reads will consume the buffered data. Upon failure to
|
|
// allocate sufficient memory to store writes, the program aborts.
|
|
//
|
|
// Calling [io::close] on this stream will free the buffer. Call [bufio::finish]
|
|
// instead to free up resources associated with the stream, but transfer
|
|
// ownership of the buffer to the caller.
|
|
export fn dynamic(mode: io::mode) *io::stream = dynamic_from([], mode);
|
|
|
|
// Like [dynamic], but takes an existing slice as input. Writes are appended to
|
|
// it and reads consume bytes from the initial buffer, plus any additional
|
|
// writes. Like [dynamic], calling [io::close] will free the buffer, and
|
|
// [bufio::finish] can be used to return ownership of the buffer to the caller.
|
|
export fn dynamic_from(in: []u8, mode: io::mode) *io::stream = {
|
|
let s = alloc(dynamic_stream {
|
|
stream = io::stream {
|
|
closer = &dynamic_close,
|
|
seeker = &dynamic_seek,
|
|
...
|
|
},
|
|
buf = in,
|
|
pos = 0,
|
|
}): *io::stream;
|
|
if (mode & io::mode::READ == io::mode::READ) {
|
|
s.reader = &dynamic_read;
|
|
};
|
|
if (mode & io::mode::WRITE == io::mode::WRITE) {
|
|
s.writer = &dynamic_write;
|
|
};
|
|
return s;
|
|
};
|
|
|
|
fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = {
|
|
let s = s: *dynamic_stream;
|
|
if (s.pos == len(s.buf)) {
|
|
append(s.buf, ...buf);
|
|
} else {
|
|
// TODO: update this after we add insert
|
|
let new: []u8 = alloc([], len(s.buf) + len(buf));
|
|
append(new, ...s.buf[..s.pos]);
|
|
append(new, ...buf[..]);
|
|
append(new, ...s.buf[s.pos..]);
|
|
free(s.buf);
|
|
s.buf = new;
|
|
};
|
|
|
|
s.pos += len(buf);
|
|
return len(buf);
|
|
};
|
|
|
|
fn dynamic_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
|
|
let s = s: *dynamic_stream;
|
|
if (len(s.buf) == s.pos && len(buf) != 0) {
|
|
return io::EOF;
|
|
};
|
|
const n = if (len(s.buf) - s.pos < len(buf)) {
|
|
len(s.buf) - s.pos;
|
|
} else {
|
|
len(buf);
|
|
};
|
|
buf[..n] = s.buf[s.pos..s.pos + n];
|
|
s.pos += n;
|
|
return n;
|
|
};
|
|
|
|
fn dynamic_seek(
|
|
s: *io::stream,
|
|
off: io::off,
|
|
w: io::whence
|
|
) (io::off | io::error) = {
|
|
let stream = s: *dynamic_stream;
|
|
switch (w) {
|
|
io::whence::SET => {
|
|
if (len(stream.buf) < off: size) {
|
|
abort("invalid offset");
|
|
};
|
|
stream.pos = off: size;
|
|
},
|
|
io::whence::CUR => {
|
|
if (stream.pos + off: size > len(stream.buf)) {
|
|
abort("invalid offset");
|
|
};
|
|
stream.pos += off: size;
|
|
},
|
|
io::whence::END => {
|
|
if (len(stream.buf) - (-off): size < len(stream.buf)) {
|
|
abort("invalid offset");
|
|
};
|
|
stream.pos = len(stream.buf) - (-off): size;
|
|
},
|
|
};
|
|
return stream.pos: io::off;
|
|
};
|
|
|
|
fn dynamic_close(s: *io::stream) void = {
|
|
const s = s: *dynamic_stream;
|
|
free(s.buf);
|
|
free(s);
|
|
};
|
|
|
|
// Closes the stream without freeing the buffer, instead transferring ownership
|
|
// of it to the caller.
|
|
export fn finish(s: *io::stream) []u8 = {
|
|
if (s.closer != &dynamic_close) {
|
|
abort("bufio::finish called on non-bufio stream");
|
|
};
|
|
let s = s: *dynamic_stream;
|
|
let buf = s.buf;
|
|
free(s);
|
|
return buf;
|
|
};
|
|
|
|
// Returns the current buffer.
|
|
export fn buffer(s: *io::stream) []u8 = {
|
|
if (s.closer != &dynamic_close) {
|
|
abort("bufio::buffer called on non-bufio stream");
|
|
};
|
|
let s = s: *dynamic_stream;
|
|
return s.buf;
|
|
};
|
|
|
|
// Resets the buffer's length to zero, but keeps the allocated memory around for
|
|
// future writes.
|
|
export fn reset(s: *io::stream) void = {
|
|
if (s.closer != &dynamic_close) {
|
|
abort("bufio::reset called on non-bufio stream");
|
|
};
|
|
const s = s: *dynamic_stream;
|
|
s.pos = 0;
|
|
s.buf = s.buf[..0];
|
|
};
|
|
|
|
// Truncates the buffer, freeing memory associated with it and setting its
|
|
// length to zero.
|
|
export fn truncate(s: *io::stream) (void | errors::unsupported) = {
|
|
if (s.closer != &dynamic_close) {
|
|
return errors::unsupported;
|
|
};
|
|
let s = s: *dynamic_stream;
|
|
s.pos = 0;
|
|
delete(s.buf[..]);
|
|
};
|
|
|
|
@test fn dynamic() void = {
|
|
// TODO: slice/array equality
|
|
let s = dynamic(io::mode::RDWR);
|
|
assert(io::write(s, [1, 2, 3]) as size == 3);
|
|
assert(bytes::equal(buffer(s), [1, 2, 3]));
|
|
assert(io::write(s, [4, 5]) as size == 2);
|
|
assert(bytes::equal(buffer(s), [1, 2, 3, 4, 5]));
|
|
let buf: [2]u8 = [0...];
|
|
assert(io::seek(s, 0, io::whence::SET) as io::off == 0: io::off);
|
|
assert(io::read(s, buf[..]) as size == 2 && bytes::equal(buf, [1, 2]));
|
|
assert(io::read(s, buf[..]) as size == 2 && bytes::equal(buf, [3, 4]));
|
|
assert(io::read(s, buf[..]) as size == 1 && buf[0] == 5);
|
|
assert(io::read(s, buf[..]) is io::EOF);
|
|
assert(io::write(s, [6, 7, 8]) as size == 3);
|
|
assert(bytes::equal(buffer(s), [1, 2, 3, 4, 5, 6, 7, 8]));
|
|
reset(s);
|
|
assert(len(buffer(s)) == 0);
|
|
assert(io::write(s, [1, 2, 3]) as size == 3);
|
|
assert(truncate(s) is void);
|
|
assert(len(buffer(s)) == 0);
|
|
|
|
let sl: []u8 = alloc([1, 2, 3]);
|
|
let s = dynamic_from(sl, io::mode::WRITE);
|
|
assert(io::write(s, [0, 0]) as size == 2);
|
|
assert(io::seek(s, 0, io::whence::END) as io::off == 5: io::off);
|
|
assert(io::write(s, [4, 5, 6]) as size == 3);
|
|
assert(bytes::equal(buffer(s), [0, 0, 1, 2, 3, 4, 5, 6]));
|
|
// TODO: this should check for errors::unsupported (harec bug prevents that)
|
|
assert(io::read(s, buf[..]) is io::error);
|
|
io::close(s);
|
|
|
|
sl = alloc([1, 2]);
|
|
let s = dynamic_from(sl, io::mode::READ);
|
|
assert(io::read(s, buf[..1]) as size == 1 && buf[0] == 1);
|
|
assert(io::seek(s, 1, io::whence::CUR) as io::off == 2: io::off);
|
|
assert(io::read(s, buf[..]) is io::EOF);
|
|
// TODO: this should check for errors::unsupported (harec bug prevents that)
|
|
assert(io::write(s, [1, 2]) is io::error);
|
|
};
|