hare

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

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:
Mio/+test/copy.ha | 36++++++++++++++++--------------------
Mio/+test/limit.ha | 22+++++++++-------------
Mio/+test/stream.ha | 39+++++++++++++++------------------------
Mio/limit.ha | 61++++++++++++++++++++++++++-----------------------------------
Mio/stream.ha | 10++++++----
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,