stream.ha (2878B)
1 // License: MPL-2.0 2 // (c) 2022 Alexey Yerin <yyp@disroot.org> 3 // (c) 2021 Drew DeVault <sir@cmpwn.com> 4 use errors; 5 use io; 6 use slices; 7 use strings; 8 9 const fixed_vtable: io::vtable = io::vtable { 10 writer = &fixed_write, 11 ... 12 }; 13 14 export type stream = struct { 15 stream: io::stream, 16 buf: []u8, 17 }; 18 19 // Returns the current contents of the buffer as a string. Aborts the program if 20 // invalid UTF-8 has been written to the buffer. The return value is borrowed 21 // from the stream, and will be freed when the stream is closed. Use 22 // [[strings::dup]] to extend its lifetime. 23 export fn string(in: *stream) str = { 24 return strings::fromutf8(in.buf)!; 25 }; 26 27 // Resets the buffer's length to zero, but does not attempt to deallocate its 28 // backing memory. Suitable for use both with fixed and dynamic streams. 29 export fn reset(in: *stream) void = { 30 in.buf = in.buf[..0]; 31 }; 32 33 // Creates a write-only string stream using the provided buffer for storage. 34 // The writes will return an error if they would exceed the buffer's capacity. 35 // The stream doesn't need to be closed. 36 export fn fixed(in: []u8) stream = { 37 return stream { 38 stream = &fixed_vtable, 39 buf = in[..0], 40 }; 41 }; 42 43 fn fixed_write(s: *io::stream, buf: const []u8) (size | io::error) = { 44 let s = s: *stream; 45 let cap = slices::cap(s.buf); 46 if (cap == len(s.buf)) return errors::overflow; 47 let n = if (cap - len(s.buf) < len(buf)) cap - len(s.buf) else len(buf); 48 static append(s.buf, buf[..n]...); 49 return n; 50 }; 51 52 @test fn fixed() void = { 53 static let buf: [1024]u8 = [0...]; 54 let stream = fixed(buf); 55 assert(string(&stream) == ""); 56 io::writeall(&stream, strings::toutf8("hello ")) as size; 57 assert(string(&stream) == "hello "); 58 io::writeall(&stream, strings::toutf8("world")) as size; 59 assert(string(&stream) == "hello world"); 60 }; 61 62 const dynamic_vtable: io::vtable = io::vtable { 63 writer = &dynamic_write, 64 closer = &dynamic_close, 65 ... 66 }; 67 68 // Creates a write-only string stream using an allocated buffer for storage, for 69 // efficiently building strings. 70 // 71 // Calling [[io::close]] on this stream will free the buffer. 72 export fn dynamic() stream = { 73 return stream { 74 stream = &dynamic_vtable, 75 buf = [], 76 }; 77 }; 78 79 // Truncates the buffer, freeing memory associated with it and setting its 80 // length to zero. 81 export fn truncate(in: *stream) void = { 82 delete(in.buf[..]); 83 }; 84 85 fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = { 86 let s = s: *stream; 87 append(s.buf, buf...); 88 return len(buf); 89 }; 90 91 fn dynamic_close(s: *io::stream) (void | io::error) = { 92 const s = s: *stream; 93 free(s.buf); 94 }; 95 96 @test fn dynamic() void = { 97 let stream = dynamic(); 98 defer io::close(&stream)!; 99 assert(string(&stream) == ""); 100 io::writeall(&stream, strings::toutf8("hello ")) as size; 101 assert(string(&stream) == "hello "); 102 io::writeall(&stream, strings::toutf8("world")) as size; 103 assert(string(&stream) == "hello world"); 104 };