hare

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

commit 4c29f54dfadb6ec656f5032e16df4135e06524a3
parent 8e99e59ae9f1e41efa432bfab6d2818c954bab07
Author: Drew DeVault <sir@cmpwn.com>
Date:   Thu, 11 Feb 2021 10:51:29 -0500

io::copy: handle short writes

Diffstat:
Mio/+test/copy.ha | 39+++++++++++++++++++++++++++++++--------
Mio/+test/stream.ha | 25+++++++++++++++++++++----
Mio/copy.ha | 14++++++++------
3 files changed, 60 insertions(+), 18 deletions(-)

diff --git a/io/+test/copy.ha b/io/+test/copy.ha @@ -1,8 +1,9 @@ fn test_copier_read(s: *stream, buf: []u8) (size | EOF | error) = { let stream = s: *test_stream; - if (stream.n == 0) { + if (stream.r == 0) { assert(len(buf) > 42); - stream.n = 42; + stream.nreads += 1; + stream.r = 42; return 42; } else { return EOF; @@ -19,19 +20,22 @@ 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; - stream.n = 62893; + stream.w = 62893; return 1337; }; fn test_copy_unsupported(a: *stream, b: *stream) (size | error) = unsupported; +fn io::println(msg: str) void; +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)) { n: size => { assert(n == 42); - assert(a.n == 42); - assert(b.n == 42); + assert(a.r == 42); + assert(b.w == 42); }, error => abort(), }; @@ -45,7 +49,7 @@ fn test_copy_unsupported(a: *stream, b: *stream) (size | error) = unsupported; match (copy(&b.stream, &a.stream)) { n: size => { assert(n == 1337); - assert(b.n == 62893); + assert(b.w == 62893); }, error => abort(), }; @@ -60,8 +64,27 @@ fn test_copy_unsupported(a: *stream, b: *stream) (size | error) = unsupported; match (copy(&b.stream, &a.stream)) { n: size => { assert(n == 42); - assert(a.n == 42); - assert(b.n == 42); + assert(a.r == 42); + assert(b.w == 42); + }, + 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)) { + n: size => { + assert(n == 42); + assert(a.r == 42); + assert(a.nreads == 1); + assert(b.w == 42); + assert(b.nwrites > 1); }, error => abort(), }; diff --git a/io/+test/stream.ha b/io/+test/stream.ha @@ -2,21 +2,38 @@ use strings; type test_stream = struct { stream: stream, - n: size, + r: size, + nreads: size, + w: size, + nwrites: size, }; fn test_stream_read(s: *stream, buf: []u8) (size | EOF | error) = { let stream = s: *test_stream; - stream.n = len(buf); + 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; - stream.n = len(buf); + 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; + stream.nwrites += 1; + if (len(buf) > 10) { + stream.w += len(buf) / 2; + return len(buf) / 2; + } else { + stream.w += len(buf); + return len(buf); + }; +}; + fn test_stream_open() test_stream = test_stream { stream = stream { name = strings::dup("test_stream"), @@ -25,7 +42,7 @@ fn test_stream_open() test_stream = test_stream { closer = &test_stream_close, ... }, - n = 0, + ... }; fn test_stream_close(s: *stream) void = { diff --git a/io/copy.ha b/io/copy.ha @@ -17,12 +17,14 @@ export fn copy(dest: *stream, src: *stream) (error | size) = { for (true) { match (read(src, buf[..])) { err: error => return err, - n: size => match (write(dest, buf[..n])) { - err: error => return err, - r: size => { - assert(r == n); // TODO? - w += n; - }, + n: size => for (let i = 0z; i < n) { + match (write(dest, buf[i..n])) { + err: error => return err, + r: size => { + w += r; + i += r; + }, + }; }, EOF => break, };