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:
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,
};