commit f73e7fbcedb0182b61a96341828839acc751d39f
parent d3b7e0ce4258beaa92cf78fd3818942c3db7c99b
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 4 Sep 2021 08:45:37 +0200
io: stack-allocated limitreader/limitwriter
This also updates io+test's teststream to work similarly.
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
5 files changed, 72 insertions(+), 96 deletions(-)
diff --git a/io/+test/copy.ha b/io/+test/copy.ha
@@ -1,7 +1,7 @@
use errors;
fn test_copier_read(s: *stream, buf: []u8) (size | EOF | error) = {
- let stream = s: *test_stream;
+ let stream = s: *teststream;
if (stream.r == 0) {
assert(len(buf) > 42);
stream.nreads += 1;
@@ -12,16 +12,16 @@ fn test_copier_read(s: *stream, buf: []u8) (size | EOF | error) = {
};
};
-fn test_copier_open() test_stream = {
- let stream = test_stream_open();
- stream.stream.reader = &test_copier_read;
+fn test_copier_open() teststream = {
+ let stream = teststream_open();
+ stream.reader = &test_copier_read;
return stream;
};
fn test_copier_copy(a: *stream, b: *stream) (size | error) = {
assert(a != b);
assert(a.reader == &test_copier_read && b.reader == &test_copier_read);
- let stream = a: *test_stream;
+ let stream = a: *teststream;
stream.w = 62893;
return 1337;
};
@@ -34,7 +34,7 @@ fn strconv::ztos(z: size) str;
@test fn copy() void = {
let a = test_copier_open(), b = test_copier_open();
- match (copy(&b.stream, &a.stream)) {
+ match (copy(&b, &a)) {
n: size => {
assert(n == 42);
assert(a.r == 42);
@@ -47,9 +47,9 @@ fn strconv::ztos(z: size) str;
a = test_copier_open();
b = test_copier_open();
- a.stream.copier = &test_copier_copy;
- b.stream.copier = &test_copier_copy;
- match (copy(&b.stream, &a.stream)) {
+ a.copier = &test_copier_copy;
+ b.copier = &test_copier_copy;
+ match (copy(&b, &a)) {
n: size => {
assert(n == 1337);
assert(b.w == 62893);
@@ -62,9 +62,9 @@ fn strconv::ztos(z: size) str;
// Fallback
a = test_copier_open();
b = test_copier_open();
- a.stream.copier = &test_copy_unsupported;
- b.stream.copier = &test_copy_unsupported;
- match (copy(&b.stream, &a.stream)) {
+ a.copier = &test_copy_unsupported;
+ b.copier = &test_copy_unsupported;
+ match (copy(&b, &a)) {
n: size => {
assert(n == 42);
assert(a.r == 42);
@@ -72,16 +72,14 @@ fn strconv::ztos(z: size) str;
},
error => abort(),
};
- close(&a: *stream);
- close(&b: *stream);
// Fallback (+short writes)
a = test_copier_open();
b = test_copier_open();
- a.stream.copier = &test_copy_unsupported;
- b.stream.copier = &test_copy_unsupported;
- b.stream.writer = &test_stream_write_short;
- match (copy(&b.stream, &a.stream)) {
+ a.copier = &test_copy_unsupported;
+ b.copier = &test_copy_unsupported;
+ b.writer = &teststream_write_short;
+ match (copy(&b, &a)) {
n: size => {
assert(n == 42);
assert(a.r == 42);
@@ -91,6 +89,4 @@ fn strconv::ztos(z: size) str;
},
error => abort(),
};
- close(&a: *stream);
- close(&b: *stream);
};
diff --git a/io/+test/limit.ha b/io/+test/limit.ha
@@ -2,37 +2,33 @@ use errors;
@test fn limit() void = {
let buf: [15z]u8 = [0...];
- let source_stream = test_stream_open();
- defer close(&source_stream.stream);
+ let source = teststream_open();
- let r_stream = limitreader(&source_stream.stream, 20);
- match (write(r_stream, buf)) {
+ let rlimit = limitreader(&source, 20);
+ match (write(&rlimit, buf)) {
errors::unsupported => void,
* => abort(),
};
- match (read(r_stream, buf)) {
+ match (read(&rlimit, buf)) {
n: size => assert(n == 15),
error => abort(),
};
- match (read(r_stream, buf)) {
+ match (read(&rlimit, buf)) {
n: size => assert(n == 5),
error => abort(),
};
- close(r_stream);
- let w_stream = limitwriter(&source_stream.stream, 20);
- match (read(w_stream, buf)) {
+ let wlimit = limitwriter(&source, 20);
+ match (read(&wlimit, buf)) {
errors::unsupported => void,
* => abort(),
};
- match (write(w_stream, buf)) {
+ match (write(&wlimit, buf)) {
n: size => assert(n == 15),
error => abort(),
};
- match (write(w_stream, buf)) {
+ match (write(&wlimit, buf)) {
n: size => assert(n == 5),
error => abort(),
};
- close(w_stream);
-
};
diff --git a/io/+test/stream.ha b/io/+test/stream.ha
@@ -1,29 +1,36 @@
use strings;
-type test_stream = struct {
- stream: stream,
+type teststream = struct {
+ stream,
r: size,
nreads: size,
w: size,
nwrites: size,
};
-fn test_stream_read(s: *stream, buf: []u8) (size | EOF | error) = {
- let stream = s: *test_stream;
+fn teststream_open() teststream = teststream {
+ name = "teststream",
+ reader = &teststream_read,
+ writer = &teststream_write,
+ ...
+};
+
+fn teststream_read(s: *stream, buf: []u8) (size | EOF | error) = {
+ let stream = s: *teststream;
stream.r += len(buf);
stream.nreads += 1;
return len(buf);
};
-fn test_stream_write(s: *stream, buf: const []u8) (size | error) = {
- let stream = s: *test_stream;
+fn teststream_write(s: *stream, buf: const []u8) (size | error) = {
+ let stream = s: *teststream;
stream.w += len(buf);
stream.nwrites += 1;
return len(buf);
};
-fn test_stream_write_short(s: *stream, buf: const []u8) (size | error) = {
- let stream = s: *test_stream;
+fn teststream_write_short(s: *stream, buf: const []u8) (size | error) = {
+ let stream = s: *teststream;
stream.nwrites += 1;
if (len(buf) > 10) {
stream.w += len(buf) / 2;
@@ -33,19 +40,3 @@ fn test_stream_write_short(s: *stream, buf: const []u8) (size | error) = {
return len(buf);
};
};
-
-fn test_stream_open() test_stream = test_stream {
- stream = stream {
- name = strings::dup("test_stream"),
- reader = &test_stream_read,
- writer = &test_stream_write,
- closer = &test_stream_close,
- ...
- },
- ...
-};
-
-fn test_stream_close(s: *stream) void = {
- let stream = s: *test_stream;
- free(stream.stream.name);
-};
diff --git a/io/limit.ha b/io/limit.ha
@@ -1,44 +1,41 @@
use strings;
-type limited_stream = struct {
- stream: stream,
+export type limitstream = struct {
+ stream,
source: *stream,
limit: size,
};
-fn limited_stream_new(source: *stream, limit: size) *limited_stream = {
- return alloc(limited_stream {
- stream = stream {
- name = strings::dup(source.name),
- closer = &limited_close,
- unwrap = &limited_unwrap,
- ...
- },
+fn limitstream_create(source: *stream, limit: size) limitstream = {
+ return limitstream {
+ name = source.name,
+ unwrap = &limit_unwrap,
source = source,
limit = limit,
- });
+ ...
+ };
};
// Create an overlay stream that only allows a limited amount of bytes to be
-// read from the underlying stream. Closing the limit stream does not close the
-// underlying stream.
-export fn limitreader(source: *stream, limit: size) *stream = {
- let stream = limited_stream_new(source, limit);
- stream.stream.reader = &limited_read;
- return &stream.stream;
+// read from the underlying stream. This stream does not need to be closed, and
+// closing it does not close the underlying stream.
+export fn limitreader(source: *stream, limit: size) limitstream = {
+ let stream = limitstream_create(source, limit);
+ stream.reader = &limit_read;
+ return stream;
};
// Create an overlay stream that only allows a limited amount of bytes to be
-// written to the underlying stream. Closing the limit stream does not close the
-// underlying stream.
-export fn limitwriter(source: *stream, limit: size) *stream = {
- let stream = limited_stream_new(source, limit);
- stream.stream.writer = &limited_write;
- return &stream.stream;
+// written to the underlying stream. This stream does not need to be closed, and
+// closing it does not close the underlying stream.
+export fn limitwriter(source: *stream, limit: size) limitstream = {
+ let stream = limitstream_create(source, limit);
+ stream.writer = &limit_write;
+ return stream;
};
-fn limited_read(s: *stream, buf: []u8) (size | EOF | error) = {
- let stream = s: *limited_stream;
+fn limit_read(s: *stream, buf: []u8) (size | EOF | error) = {
+ let stream = s: *limitstream;
if (len(buf) > stream.limit) {
buf = buf[..stream.limit];
};
@@ -46,8 +43,8 @@ fn limited_read(s: *stream, buf: []u8) (size | EOF | error) = {
return read(stream.source, buf);
};
-fn limited_write(s: *stream, buf: const []u8) (size | error) = {
- let stream = s: *limited_stream;
+fn limit_write(s: *stream, buf: const []u8) (size | error) = {
+ let stream = s: *limitstream;
let slice = if (len(buf) > stream.limit) {
yield buf[..stream.limit];
} else {
@@ -57,13 +54,7 @@ fn limited_write(s: *stream, buf: const []u8) (size | error) = {
return write(stream.source, slice);
};
-fn limited_close(s: *stream) void = {
- free(s.name);
- free(s);
-};
-
-fn limited_unwrap(s: *stream) *io::stream = {
- assert(s.unwrap == &limited_unwrap);
- let s = s: *limited_stream;
+fn limit_unwrap(s: *stream) *io::stream = {
+ let s = s: *limitstream;
return s.source;
};
diff --git a/io/stream.ha b/io/stream.ha
@@ -4,14 +4,14 @@ use errors;
// operations. To create a custom stream, embed this type as the first member of
// a struct with user-specific data and fill out these fields as appropriate.
//
-// type my_stream = struct {
+// export type my_stream = struct {
// io::stream,
// fd: int,
// };
//
-// fn open(path: str) *io::stream = {
+// export fn open(path: str) my_stream = {
// let fd = // ...
-// let stream = alloc(*my_stream, my_stream {
+// return my_stream {
// name = strings::dup(path),
// reader = &my_stream_read,
// writer = &my_stream_write,
@@ -19,8 +19,10 @@ use errors;
// fd: fd,
// ...
// });
-// return &stream.stream;
// };
+//
+// let stream = open("example");
+// io::read(&stream, buf)!;
export type stream = struct {
name: str,
reader: nullable *reader,