commit ab0f65ccefd2b00ff38a9df755d596bfb0486148
parent b31e1448a36dec44480b3414a7da3b56f9048fc9
Author: Drew DeVault <sir@cmpwn.com>
Date: Thu, 25 Feb 2021 14:00:49 -0500
strio: add strio::dynamic
Diffstat:
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");
+};