hare

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

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