hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit ab0f65ccefd2b00ff38a9df755d596bfb0486148
parent b31e1448a36dec44480b3414a7da3b56f9048fc9
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu, 25 Feb 2021 14:00:49 -0500

strio: add strio::dynamic

Diffstat:
Mbufio/dynamic.ha | 42+++++++++++++++++++++---------------------
Dstrio/buffer.ha | 51---------------------------------------------------
Astrio/dynamic.ha | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astrio/fixed.ha | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 143 insertions(+), 72 deletions(-)

diff --git a/bufio/dynamic.ha b/bufio/dynamic.ha @@ -6,6 +6,23 @@ type dynamic_stream = struct { buf: []u8, }; +// 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() *io::stream = alloc(dynamic_stream { + stream = io::stream { + writer = &dynamic_write, + closer = &dynamic_close, + reader = &dynamic_read, + ... + }, + buf = [], +}): *io::stream; + fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = { let s = s: *dynamic_stream; append(s.buf, ...buf); @@ -28,20 +45,20 @@ fn dynamic_close(s: *io::stream) void = { // Closes the stream without freeing the buffer, instead transferring ownership // of it to the caller. -export fn finish(s: *io::stream) ([]u8 | io::unsupported) = { +export fn finish(s: *io::stream) []u8 = { if (s.writer != &dynamic_write || s.closer != &dynamic_close) { - return io::unsupported; + abort("bufio::finish called on non-bufio stream"); }; let s = s: *dynamic_stream; let buf = s.buf; free(s); - return s.buf; + return buf; }; // Returns the current buffer. export fn buffer(s: *io::stream) ([]u8 | io::unsupported) = { if (s.writer != &dynamic_write || s.closer != &dynamic_close) { - return io::unsupported; + abort("bufio::buffer called on non-bufio stream"); }; let s = s: *dynamic_stream; return s.buf; @@ -67,23 +84,6 @@ export fn truncate(s: *io::stream) (void | io::unsupported) = { delete(s.buf[..]); }; -// 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() *io::stream = alloc(dynamic_stream { - stream = io::stream { - writer = &dynamic_write, - closer = &dynamic_close, - reader = &dynamic_read, - ... - }, - buf = [], -}): *io::stream; - @test fn dynamic() void = { // TODO: slice/array equality let s = dynamic(); diff --git a/strio/buffer.ha b/strio/buffer.ha @@ -1,51 +0,0 @@ -use io; -use strings; - -type fixed_stream = struct { - stream: io::stream, - buf: []u8, - cur: []u8, -}; - -// Creates a write-only string stream using the provided buffer for storage. -// The program aborts if writes would exceed the buffer's capacity. -export fn fixed(in: []u8) *io::stream = { - let s = alloc(fixed_stream { - stream = io::stream { - name = "<strio::fixed>", - writer = &fixed_write, - ... - }, - buf = in, - cur = in, - }); - return &s.stream; -}; - -// Returns the current contents of the buffer as a string. Aborts the program if -// invalid UTF-8 has been written to the buffer. -export fn string(s: *io::stream) str = { - assert(s.writer == &fixed_write); - let stream = s: *fixed_stream; - const n = len(stream.buf) - len(stream.cur); - return strings::from_utf8(stream.buf[..n]); -}; - -fn fixed_write(s: *io::stream, buf: const []u8) (size | io::error) = { - let stream = s: *fixed_stream; - if (len(stream.cur) == 0) { - abort("strio::fixed buffer exceeded"); - }; - const n = if (len(buf) > len(stream.cur)) len(stream.cur) else len(buf); - stream.cur[..n] = buf[..n]; - stream.cur = stream.cur[n..]; - return n; -}; - -@test fn fixed() void = { - static let buf: [1024]u8 = [0...]; - let stream = fixed(buf); - io::write(stream, strings::to_utf8("hello ")) as size; - io::write(stream, strings::to_utf8("world")) as size; - assert(string(stream) == "hello world"); -}; diff --git a/strio/dynamic.ha b/strio/dynamic.ha @@ -0,0 +1,59 @@ +use io; +use strings; + +type dynamic_stream = struct { + stream: io::stream, + buf: []u8, +}; + +// Creates a write-only string stream using an allocated buffer for storage, for +// efficiently building strings. +// +// Calling [io::close] on this stream will free the buffer. Call [strio::finish] +// instead to free up resources associated with the stream, but transfer +// ownership of the buffer to the caller. +export fn dynamic() *io::stream = { + let s = alloc(dynamic_stream { + stream = io::stream { + name = "<strio::dynamic>", + writer = &dynamic_write, + closer = &dynamic_close, + ... + }, + buf = [], + }); + return &s.stream; +}; + +// Closes the stream without freeing the buffer, instead transferring ownership +// of it to the caller. +export fn finish(s: *io::stream) str = { + assert(s.writer == &dynamic_write, + "strio::finish called on non-strio stream"); + let s = s: *dynamic_stream; + let buf = s.buf; + free(s); + return strings::from_utf8(buf); +}; + +fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = { + let s = s: *dynamic_stream; + append(s.buf, ...buf); + return len(buf); +}; + +fn dynamic_close(s: *io::stream) void = { + const s = s: *dynamic_stream; + free(s.buf); + free(s); +}; + +@test fn dynamic() void = { + let stream = dynamic(); + io::write(stream, strings::to_utf8("hello ")) as size; + io::write(stream, strings::to_utf8("world")) as size; + assert(string(stream) == "hello world"); + let s = finish(stream); + assert(s == "hello world"); + free(s); +}; diff --git a/strio/fixed.ha b/strio/fixed.ha @@ -0,0 +1,63 @@ +use io; +use strings; + +type fixed_stream = struct { + stream: io::stream, + buf: []u8, + cur: []u8, +}; + +// Creates a write-only string stream using the provided buffer for storage. +// The program aborts if writes would exceed the buffer's capacity. +export fn fixed(in: []u8) *io::stream = { + let s = alloc(fixed_stream { + stream = io::stream { + name = "<strio::fixed>", + writer = &fixed_write, + closer = &fixed_close, + ... + }, + buf = in, + cur = in, + }); + return &s.stream; +}; + +// Returns the current contents of the buffer as a string. Aborts the program if +// invalid UTF-8 has been written to the buffer. +export fn string(s: *io::stream) str = { + if (s.writer == &fixed_write) { + let stream = s: *fixed_stream; + const n = len(stream.buf) - len(stream.cur); + return strings::from_utf8(stream.buf[..n]); + } else if (s.writer == &dynamic_write) { + let s = s: *dynamic_stream; + let buf = s.buf; + return strings::from_utf8(buf); + } else { + abort("strio::string called on non-strio stream"); + }; +}; + +fn fixed_write(s: *io::stream, buf: const []u8) (size | io::error) = { + let stream = s: *fixed_stream; + if (len(stream.cur) == 0) { + abort("strio::fixed buffer exceeded"); + }; + const n = if (len(buf) > len(stream.cur)) len(stream.cur) else len(buf); + stream.cur[..n] = buf[..n]; + stream.cur = stream.cur[n..]; + return n; +}; + +fn fixed_close(s: *io::stream) void = { + free(s); +}; + +@test fn fixed() void = { + static let buf: [1024]u8 = [0...]; + let stream = fixed(buf); + io::write(stream, strings::to_utf8("hello ")) as size; + io::write(stream, strings::to_utf8("world")) as size; + assert(string(stream) == "hello world"); +};