hare

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

commit fba5c36a64ffcf04660dc7fe0449bb267a783ea0
parent f6760e9e25d5607c430e6b204ea80f2db68679f0
Author: Alexey Yerin <yyp@disroot.org>
Date:   Sun,  2 Jan 2022 12:59:55 +0300

strio: use caller allocation

Instead of allocated io::handle strio::dynamic() returns dynamic_stream
and strio::fixed() returns fixed_stream.

Also, strio::finish() is gone and should be replaced by strio::string(),
since the stream itself doesn't need to be freed.

Another update necessary is adding '&' to uses of strio streams:

    let s = strio::dynamic();

    // Previously: fmt::fprint(s, "Hiya")!;
    fmt::fprint(&s, "Hiya")!;

    // io::close(s);
    io::close(&s);

Signed-off-by: Alexey Yerin <yyp@disroot.org>

Diffstat:
Mcmd/haredoc/docstr.ha | 20++++++++++----------
Mcmd/haredoc/html.ha | 38+++++++++++++++++++-------------------
Mcrypto/blake2b/+test.ha | 13+++++++------
Mencoding/base64/base64.ha | 4++--
Mencoding/hex/hex.ha | 9+++++----
Mformat/xml/parser.ha | 50+++++++++++++++++++++++++-------------------------
Mformat/xml/types.ha | 7++++---
Mhare/lex/lex.ha | 18+++++++++---------
Mhare/module/context.ha | 4++--
Mhare/module/scan.ha | 4++--
Mhare/parse/+test/roundtrip.ha | 6+++---
Mhare/parse/parse.ha | 8++++----
Mhare/unparse/decl.ha | 4++--
Mhare/unparse/ident.ha | 4++--
Mhare/unparse/import.ha | 12++++++------
Mhare/unparse/type.ha | 4++--
Mnet/ip/ip.ha | 4++--
Mshlex/split.ha | 12++++++------
Mstrio/dynamic.ha | 54++++++++++++++----------------------------------------
Mstrio/fixed.ha | 48++++++++++++++++--------------------------------
Mstrio/ops.ha | 44++++++++++++++++++++++----------------------
Mtemp/+freebsd.ha | 5++---
Mtemp/+linux.ha | 5++---
Muuid/uuid.ha | 10++++------
24 files changed, 172 insertions(+), 215 deletions(-)

diff --git a/cmd/haredoc/docstr.ha b/cmd/haredoc/docstr.ha @@ -82,7 +82,7 @@ fn scantext(par: *parser) (token | void) = { bufio::unreadrune(par.src, rn); break; case '\n' => - strio::appendrune(buf, rn)!; + strio::appendrune(&buf, rn)!; const rn = match (bufio::scanrune(par.src)!) { case io::EOF => break; case let rn: rune => @@ -94,10 +94,10 @@ fn scantext(par: *parser) (token | void) = { }; bufio::unreadrune(par.src, rn); case => - strio::appendrune(buf, rn)!; + strio::appendrune(&buf, rn)!; }; }; - let result = strio::finish(buf); + let result = strio::string(&buf); if (len(result) == 0) { return; }; @@ -124,7 +124,7 @@ fn scanref(par: *parser) (token | void) = { }; const buf = strio::dynamic(); - defer io::close(buf); + defer io::close(&buf); // TODO: Handle invalid syntax here for (true) { match (bufio::scanrune(par.src)!) { @@ -134,12 +134,12 @@ fn scanref(par: *parser) (token | void) = { bufio::scanrune(par.src) as rune; // ] break; case => - strio::appendrune(buf, rn)!; + strio::appendrune(&buf, rn)!; }; case io::EOF => break; }; }; - let id = parse::identstr(strio::string(buf)) as ast::ident; + let id = parse::identstr(strio::string(&buf)) as ast::ident; return id: reference; }; @@ -175,9 +175,9 @@ fn scansample(par: *parser) (token | void) = { }; switch (rn) { case '\n' => - strio::appendrune(buf, rn)!; + strio::appendrune(&buf, rn)!; case => - strio::appendrune(buf, rn)!; + strio::appendrune(&buf, rn)!; continue; }; @@ -192,7 +192,7 @@ fn scansample(par: *parser) (token | void) = { case '\t' => i += 8; case '\n' => - strio::appendrune(buf, rn)!; + strio::appendrune(&buf, rn)!; i = 0; case => bufio::unreadrune(par.src, rn); @@ -203,7 +203,7 @@ fn scansample(par: *parser) (token | void) = { }; }; - let buf = strio::finish(buf); + let buf = strio::string(&buf); // Trim trailing newlines for (strings::hassuffix(buf, "\n")) { diff --git a/cmd/haredoc/html.ha b/cmd/haredoc/html.ha @@ -42,19 +42,19 @@ fn html_escape(out: io::handle, in: str) (size | io::error) = { @test fn html_escape() void = { let sink = strio::dynamic(); - defer io::close(sink); - html_escape(sink, "hello world!")!; - assert(strio::string(sink) == "hello world!"); + defer io::close(&sink); + html_escape(&sink, "hello world!")!; + assert(strio::string(&sink) == "hello world!"); let sink = strio::dynamic(); - defer io::close(sink); - html_escape(sink, "\"hello world!\"")!; - assert(strio::string(sink) == "&quot;hello world!&quot;"); + defer io::close(&sink); + html_escape(&sink, "\"hello world!\"")!; + assert(strio::string(&sink) == "&quot;hello world!&quot;"); let sink = strio::dynamic(); - defer io::close(sink); - html_escape(sink, "<hello & 'world'!>")!; - assert(strio::string(sink) == "&lt;hello &amp; &apos;world&apos;!&gt;"); + defer io::close(&sink); + html_escape(&sink, "<hello & 'world'!>")!; + assert(strio::string(&sink) == "&lt;hello &amp; &apos;world&apos;!&gt;"); }; // Formats output as HTML @@ -66,7 +66,7 @@ fn emit_html(ctx: *context) (void | error) = { if (ctx.template) head(ctx.ident)?; if (len(ident) == 0) { - fmt::fprintf(ctx.out, + fmt::fprintf(ctx.out, "<h2>The Hare standard library <span class='heading-extra'>", ident)?; } else { @@ -567,10 +567,10 @@ fn type_html( brief: bool, ) (size | io::error) = { let buf = strio::dynamic(); - defer io::close(buf); - unparse::_type(buf, indent, _type)?; + defer io::close(&buf); + unparse::_type(&buf, indent, _type)?; if (brief) { - return html_escape(out, strio::string(buf))?; + return html_escape(out, strio::string(&buf))?; }; // TODO: More detailed formatter which can find aliases nested deeper in @@ -711,14 +711,14 @@ fn breadcrumb(ident: ast::ident) str = { return ""; }; let buf = strio::dynamic(); - fmt::fprintf(buf, "<a href='/'>stdlib</a> » ")!; + fmt::fprintf(&buf, "<a href='/'>stdlib</a> » ")!; for (let i = 0z; i < len(ident) - 1; i += 1) { let ipath = module::identpath(ident[..i+1]); defer free(ipath); - fmt::fprintf(buf, "<a href='/{}'>{}</a>::", ipath, ident[i])!; + fmt::fprintf(&buf, "<a href='/{}'>{}</a>::", ipath, ident[i])!; }; - fmt::fprint(buf, ident[len(ident) - 1])!; - return strio::finish(buf); + fmt::fprint(&buf, ident[len(ident) - 1])!; + return strio::string(&buf); }; fn head(ident: ast::ident) (void | error) = { @@ -731,7 +731,7 @@ fn head(ident: ast::ident) (void | error) = { const title = if (len(id) == 0) fmt::asprintf("Hare documentation") - else + else fmt::asprintf("{} — Hare documentation", id); defer free(title); @@ -848,7 +848,7 @@ h4:target + pre { details { background: #eee; - margin: 1rem -1rem 1rem; + margin: 1rem -1rem 1rem; } summary { diff --git a/crypto/blake2b/+test.ha b/crypto/blake2b/+test.ha @@ -23,8 +23,9 @@ use strio; }; hash::finish(&blake, sum); let out = strio::dynamic(); - hex::encode(out, sum)!; - assert(strio::string(out) == vectors[i].out); + defer io::close(&out); + hex::encode(&out, sum)!; + assert(strio::string(&out) == vectors[i].out); }; let blake = blake2b([], 64); @@ -49,15 +50,15 @@ use strio; hash::sum(&blake, sum); let hex = strio::dynamic(); - defer io::close(hex); + defer io::close(&hex); for (let j = 0z; j < len(sum); j += 1) { - fmt::fprintf(hex, "{:02x}", sum[j])!; + fmt::fprintf(&hex, "{:02x}", sum[j])!; }; - if (strio::string(hex) != vector.1) { + if (strio::string(&hex) != vector.1) { fmt::errorfln("Vector {}: {} != {}", - i, strio::string(hex), vector.1)!; + i, strio::string(&hex), vector.1)!; abort(); }; }; diff --git a/encoding/base64/base64.ha b/encoding/base64/base64.ha @@ -73,8 +73,8 @@ export fn encode( // returns it. The caller must free the return value. export fn encodestr(alphabet: []u8, b: []u8) str = { let sink = strio::dynamic(); - encode(alphabet, sink, b) as size; - return strio::finish(sink); + encode(alphabet, &sink, b) as size; + return strio::string(&sink); }; @test fn encode() void = { diff --git a/encoding/hex/hex.ha b/encoding/hex/hex.ha @@ -27,8 +27,8 @@ export fn encode(sink: io::handle, b: []u8) (size | io::error) = { // free the return value. export fn encodestr(b: []u8) str = { let sink = strio::dynamic(); - encode(sink, b) as size; - return strio::finish(sink); + encode(&sink, b) as size; + return strio::string(&sink); }; @test fn encode() void = { @@ -111,9 +111,10 @@ export fn dump(out: io::handle, data: []u8) (void | io::error) = { ]; let sink = strio::dynamic(); - dump(sink, in) as void; + defer io::close(&sink); + dump(&sink, in) as void; - let s = strio::finish(sink); + let s = strio::string(&sink); assert(s == "00000000 7f 45 4c 46 02 01 01 00 ca fe ba be de ad f0 0d |.ELF............|\n" "00000010 ce fe ba be de ad f0 0d |........|\n"); diff --git a/format/xml/parser.ha b/format/xml/parser.ha @@ -43,9 +43,9 @@ export fn parser_free(par: *parser) void = { if (par.close) { io::close(par.in); }; - io::close(par.namebuf); - io::close(par.entbuf); - io::close(par.textbuf); + io::close(&par.namebuf); + io::close(&par.entbuf); + io::close(&par.textbuf); for (let i = 0z; i < len(par.tags); i += 1) { free(par.tags[i]); }; @@ -126,16 +126,16 @@ fn poptag(par: *parser, expect: str) (str | error) = { if (expect != "" && expect != pop) { return syntaxerr; }; - strio::reset(par.namebuf); - strio::concat(par.namebuf, pop)!; - return strio::string(par.namebuf); + strio::reset(&par.namebuf); + strio::concat(&par.namebuf, pop)!; + return strio::string(&par.namebuf); }; fn scan_attr(par: *parser) (token | error) = { - let name = scan_name(par, par.namebuf)?; + let name = scan_name(par, &par.namebuf)?; want(par, OPTWS, '=', OPTWS)?; let quot = quote(par)?; - strio::reset(par.textbuf); + strio::reset(&par.textbuf); for (true) match (bufio::scanrune(par.in)?) { case io::EOF => return syntaxerr; @@ -150,9 +150,9 @@ fn scan_attr(par: *parser) (token | error) = { yield rn; }; if (rn == quot) break; - strio::appendrune(par.textbuf, rn)?; + strio::appendrune(&par.textbuf, rn)?; }; - return (name, strio::string(par.textbuf)): attribute; + return (name, strio::string(&par.textbuf)): attribute; }; fn scan_comment(par: *parser) (token | void | error) = { @@ -201,7 +201,7 @@ fn scan_comment(par: *parser) (token | void | error) = { }; fn scan_cdata(par: *parser) (text | error) = { - strio::reset(par.textbuf); + strio::reset(&par.textbuf); for (true) { const rn = match (bufio::scanrune(par.in)?) { case io::EOF => @@ -210,7 +210,7 @@ fn scan_cdata(par: *parser) (text | error) = { yield rn; }; if (rn != ']') { - strio::appendrune(par.textbuf, rn)!; + strio::appendrune(&par.textbuf, rn)!; continue; }; const rn = match (bufio::scanrune(par.in)?) { @@ -220,7 +220,7 @@ fn scan_cdata(par: *parser) (text | error) = { yield rn; }; if (rn != ']') { - strio::appendrune(par.textbuf, rn)!; + strio::appendrune(&par.textbuf, rn)!; continue; }; const rn = match (bufio::scanrune(par.in)?) { @@ -230,13 +230,13 @@ fn scan_cdata(par: *parser) (text | error) = { yield rn; }; if (rn == '>') break; - strio::appendrune(par.textbuf, rn)!; + strio::appendrune(&par.textbuf, rn)!; }; - return strio::string(par.textbuf): text; + return strio::string(&par.textbuf): text; }; fn scan_content(par: *parser) (text | error) = { - strio::reset(par.textbuf); + strio::reset(&par.textbuf); for (true) match (bufio::scanrune(par.in)?) { case io::EOF => break; @@ -251,9 +251,9 @@ fn scan_content(par: *parser) (text | error) = { case => yield rn; }; - strio::appendrune(par.textbuf, rn)?; + strio::appendrune(&par.textbuf, rn)?; }; - return strio::string(par.textbuf); + return strio::string(&par.textbuf); }; fn scan_element(par: *parser) (token | error) = { @@ -270,7 +270,7 @@ fn scan_element(par: *parser) (token | error) = { bufio::unreadrune(par.in, rn); }; }; - let name = scan_name(par, par.namebuf)?; + let name = scan_name(par, &par.namebuf)?; if (close) { poptag(par, name)?; return name: elementend; @@ -312,7 +312,7 @@ fn scan_charref(par: *parser) (rune | error) = { }; }; - strio::reset(par.entbuf); + strio::reset(&par.entbuf); for (true) { let rn = match (bufio::scanrune(par.in)?) { case io::EOF => @@ -321,17 +321,17 @@ fn scan_charref(par: *parser) (rune | error) = { yield rn; }; if (ascii::isdigit(rn)) { - strio::appendrune(par.entbuf, rn)?; + strio::appendrune(&par.entbuf, rn)?; } else if (rn == ';') { break; } else { return syntaxerr; }; }; - if (len(strio::string(par.entbuf)) == 0) { + if (len(strio::string(&par.entbuf)) == 0) { return syntaxerr; }; - match (strconv::stou32b(strio::string(par.entbuf), base)) { + match (strconv::stou32b(strio::string(&par.entbuf), base)) { case let u: u32 => return u: rune; case (strconv::invalid | strconv::overflow) => @@ -340,7 +340,7 @@ fn scan_charref(par: *parser) (rune | error) = { }; fn scan_namedent(par: *parser) (rune | error) = { - const name = scan_name(par, par.entbuf)?; + const name = scan_name(par, &par.entbuf)?; want(par, ';')?; const map = [ ("lt", '<'), @@ -359,7 +359,7 @@ fn scan_namedent(par: *parser) (rune | error) = { return syntaxerr; }; -fn scan_name(par: *parser, buf: io::handle) (str | error) = { +fn scan_name(par: *parser, buf: *strio::dynamic_stream) (str | error) = { strio::reset(buf); const rn = match (bufio::scanrune(par.in)?) { diff --git a/format/xml/types.ha b/format/xml/types.ha @@ -1,6 +1,7 @@ use encoding::utf8; use io; use os; +use strio; export type parser = struct { in: io::handle, @@ -10,9 +11,9 @@ export type parser = struct { tags: []str, // strio buffers: - namebuf: io::handle, - entbuf: io::handle, - textbuf: io::handle, + namebuf: strio::dynamic_stream, + entbuf: strio::dynamic_stream, + textbuf: strio::dynamic_stream, }; export type state = enum { diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha @@ -229,7 +229,7 @@ fn lex_string(lex: *lexer, loc: location) (token | error) = { else { unget(lex, r); let r = lex_rune(lex, loc)?; - strio::appendrune(buf, r)?; + strio::appendrune(&buf, r)?; }; }; match (nextw(lex)?) { @@ -238,13 +238,13 @@ fn lex_string(lex: *lexer, loc: location) (token | error) = { if (r.0 == '"') { const tok = lex_string(lex, loc)?; const next = tok.1 as str; - strio::concat(buf, next)!; + strio::concat(&buf, next)!; free(next); } else { unget(lex, r); }; }; - return (ltok::LIT_STR, strio::finish(buf), loc); + return (ltok::LIT_STR, strio::string(&buf), loc); }; fn lex_rn_str(lex: *lexer) (token | error) = { @@ -281,7 +281,7 @@ fn lex_name(lex: *lexer, loc: location, label: bool) (token | error) = { match (next(lex)) { case let r: (rune, location) => assert(is_name(r.0, false)); - strio::appendrune(buf, r.0)!; + strio::appendrune(&buf, r.0)!; case (io::EOF | io::error) => abort(); }; @@ -293,10 +293,10 @@ fn lex_name(lex: *lexer, loc: location, label: bool) (token | error) = { unget(lex, r); break; }; - strio::appendrune(buf, r.0)?; + strio::appendrune(&buf, r.0)?; }; - let n = strio::finish(buf); + let n = strio::string(&buf); if (label) { return (ltok::LABEL, n, loc); }; @@ -327,17 +327,17 @@ fn lex_comment(lexr: *lexer) (token | error) = { }; let buf = strio::dynamic(); - defer io::close(buf); + defer io::close(&buf); for (true) match (next(lexr)?) { case io::EOF => break; case let r: (rune, location) => - strio::appendrune(buf, r.0)!; + strio::appendrune(&buf, r.0)!; if (r.0 == '\n') { break; }; }; - let new = strings::concat(lexr.comment, strio::string(buf)); + let new = strings::concat(lexr.comment, strio::string(&buf)); free(lexr.comment); lexr.comment = new; return lex(lexr); diff --git a/hare/module/context.ha b/hare/module/context.ha @@ -99,11 +99,11 @@ export fn identpath(name: ast::ident) str = { export fn identuscore(ident: ast::ident) str = { let buf = strio::dynamic(); for (let i = 0z; i < len(ident); i += 1) { - fmt::fprintf(buf, "{}{}", ident[i], + fmt::fprintf(&buf, "{}{}", ident[i], if (i + 1 < len(ident)) "_" else "") as size; }; - return strio::finish(buf); + return strio::string(&buf); }; @test fn identuscore() void = { diff --git a/hare/module/scan.ha b/hare/module/scan.ha @@ -419,13 +419,13 @@ export fn parsetags(in: str) ([]tag | void) = { break; case let r: rune => if (ascii::isalnum(r) || r == '_') { - strio::appendrune(buf, r)!; + strio::appendrune(&buf, r)!; } else { strings::push(&iter, r); break; }; }; - t.name = strio::finish(buf); + t.name = strio::string(&buf); append(tags, t); }; return tags; diff --git a/hare/parse/+test/roundtrip.ha b/hare/parse/+test/roundtrip.ha @@ -24,9 +24,9 @@ fn roundtrip(src: str) void = { }; defer ast::subunit_free(u); let out = strio::dynamic(); - unparse::subunit(out, u) as size; - let unsrc = strio::finish(out); - defer free(unsrc); + defer io::close(&out); + unparse::subunit(&out, u) as size; + let unsrc = strio::string(&out); if (unsrc != src) { fmt::errorfln("=== wanted\n{}", src)!; fmt::errorfln("=== got\n{}", unsrc)!; diff --git a/hare/parse/parse.ha b/hare/parse/parse.ha @@ -34,16 +34,16 @@ fn want(lexer: *lex::lexer, want: lex::ltok...) (lex::token | error) = { }; let buf = strio::dynamic(); - defer io::close(buf); + defer io::close(&buf); for (let i = 0z; i < len(want); i += 1) { const tstr = lex::tokstr((want[i], void, lex::mkloc(lexer))); - fmt::fprintf(buf, "'{}'", tstr)!; + fmt::fprintf(&buf, "'{}'", tstr)!; if (i + 1 < len(want)) { - fmt::fprint(buf, ", ")!; + fmt::fprint(&buf, ", ")!; }; }; return syntaxerr(lex::mkloc(lexer), "Unexpected '{}', was expecting {}", - lex::tokstr(tok), strio::string(buf)); + lex::tokstr(tok), strio::string(&buf)); }; // Looks for a matching ltok from the lexer, and if not present, unlexes the diff --git a/hare/unparse/decl.ha b/hare/unparse/decl.ha @@ -89,8 +89,8 @@ export fn decl(out: io::handle, d: ast::decl) (size | io::error) = { fn decl_test(d: ast::decl, expected: str) bool = { let buf = strio::dynamic(); - decl(buf, d) as size; - let s = strio::finish(buf); + decl(&buf, d) as size; + let s = strio::string(&buf); defer free(s); return s == expected; }; diff --git a/hare/unparse/ident.ha b/hare/unparse/ident.ha @@ -17,8 +17,8 @@ export fn ident(out: io::handle, id: ast::ident) (size | io::error) = { // Unparses an identifier into a string. The caller must free the return value. export fn identstr(id: ast::ident) str = { let buf = strio::dynamic(); - ident(buf, id)!; - return strio::finish(buf); + ident(&buf, id)!; + return strio::string(&buf); }; @test fn ident() void = { diff --git a/hare/unparse/import.ha b/hare/unparse/import.ha @@ -29,24 +29,24 @@ export fn import(out: io::handle, i: ast::import) (size | io::error) = { @test fn import() void = { let buf = strio::dynamic(); - import(buf, ["foo", "bar", "baz"]) as size; - let s = strio::finish(buf); + import(&buf, ["foo", "bar", "baz"]) as size; + let s = strio::string(&buf); assert(s == "use foo::bar::baz;"); free(s); buf = strio::dynamic(); - import(buf, ast::import_alias { + import(&buf, ast::import_alias { ident = ["foo"], alias = "bar", }) as size; - s = strio::finish(buf); + s = strio::string(&buf); assert(s == "use bar = foo;"); free(s); buf = strio::dynamic(); - import(buf, ast::import_objects { + import(&buf, ast::import_objects { ident = ["foo"], objects = ["bar", "baz"], }) as size; - s = strio::finish(buf); + s = strio::string(&buf); assert(s == "use foo::{bar, baz};"); free(s); }; diff --git a/hare/unparse/type.ha b/hare/unparse/type.ha @@ -224,8 +224,8 @@ export fn _type( fn type_test(t: ast::_type, expected: str) bool = { let buf = strio::dynamic(); - _type(buf, 0, t) as size; - let s = strio::finish(buf); + _type(&buf, 0, t) as size; + let s = strio::string(&buf); defer free(s); return s == expected; }; diff --git a/net/ip/ip.ha b/net/ip/ip.ha @@ -338,8 +338,8 @@ export fn string(item: (...addr | subnet)) str = { // Maximum length of an IPv6 address plus its netmask in hexadecimal static let buf: [64]u8 = [0...]; let stream = strio::fixed(buf); - fmt(stream, item) as size; - return strio::string(stream); + fmt(&stream, item) as size; + return strio::string(&stream); }; fn wanttoken(tok: *strings::tokenizer) (str | invalid) = { diff --git a/shlex/split.ha b/shlex/split.ha @@ -36,18 +36,18 @@ export fn split(in: const str) ([]str | syntaxerr) = { break; }; if (!first) { - append(slice, strio::finish(s)); + append(slice, strio::string(&s)); s = strio::dynamic(); }; dirty = false; case '\\' => - scan_backslash(s, &iter)?; + scan_backslash(&s, &iter)?; case '"' => - scan_double(s, &iter)?; + scan_double(&s, &iter)?; case '\'' => - scan_single(s, &iter)?; + scan_single(&s, &iter)?; case => - strio::appendrune(s, r)!; + strio::appendrune(&s, r)!; }; if (first) { @@ -56,7 +56,7 @@ export fn split(in: const str) ([]str | syntaxerr) = { }; if (dirty) { - append(slice, strio::finish(s)); + append(slice, strio::string(&s)); }; return slice; diff --git a/strio/dynamic.ha b/strio/dynamic.ha @@ -2,7 +2,7 @@ use errors; use io; use strings; -type dynamic_stream = struct { +export type dynamic_stream = struct { stream: io::stream, buf: []u8, }; @@ -10,53 +10,30 @@ type dynamic_stream = struct { // Creates a write-only string stream using an allocated buffer for storage, for // efficiently building strings. // -// Calling [[io::close]] on this stream will free the buffer. Call [[strio::finish]] -// instead to free up resources associated with the stream, but transfer -// ownership of the buffer to the caller. -export fn dynamic() io::handle = { - let s = alloc(dynamic_stream { +// Calling [[io::close]] on this stream will free the buffer. If stream's data +// is transfered via [[string]], it shouldn't be closed as long if the data is +// freed. +export fn dynamic() dynamic_stream = { + return dynamic_stream { stream = io::stream { writer = &dynamic_write, closer = &dynamic_close, ... }, buf = [], - }); - return &s.stream; -}; - -fn get_dynamic_stream(in: io::handle) *dynamic_stream = { - match (in) { - case io::file => - abort("Invalid use of strio on io::file"); - case let st: *io::stream => - assert(st.writer == &dynamic_write, - "Invalid use of strio on non-strio I/O stream"); - return st: *dynamic_stream; }; }; -// Closes the stream without freeing the buffer, instead transferring ownership -// of it to the caller. -export fn finish(in: io::handle) str = { - let s = get_dynamic_stream(in); - let buf = s.buf; - free(s); - return strings::fromutf8(buf); -}; - // Resets the buffer's length to zero, but keeps the allocated memory around for // future writes. -export fn reset(in: io::handle) void = { - const s = get_dynamic_stream(in); - s.buf = s.buf[..0]; +export fn reset(in: *dynamic_stream) void = { + in.buf = in.buf[..0]; }; // Truncates the buffer, freeing memory associated with it and setting its // length to zero. -export fn truncate(in: io::handle) void = { - let s = get_dynamic_stream(in); - delete(s.buf[..]); +export fn truncate(in: *dynamic_stream) void = { + delete(in.buf[..]); }; fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = { @@ -68,15 +45,12 @@ fn dynamic_write(s: *io::stream, buf: const []u8) (size | io::error) = { fn dynamic_close(s: *io::stream) void = { const s = s: *dynamic_stream; free(s.buf); - free(s); }; @test fn dynamic() void = { let stream = dynamic(); - io::write(stream, strings::toutf8("hello ")) as size; - io::write(stream, strings::toutf8("world")) as size; - assert(string(stream) == "hello world"); - let s = finish(stream); - assert(s == "hello world"); - free(s); + defer io::close(&stream); + io::write(&stream, strings::toutf8("hello ")) as size; + io::write(&stream, strings::toutf8("world")) as size; + assert(string(&stream) == "hello world"); }; diff --git a/strio/fixed.ha b/strio/fixed.ha @@ -1,46 +1,35 @@ use io; use strings; -type fixed_stream = struct { +export type fixed_stream = struct { stream: io::stream, buf: []u8, cur: []u8, }; // Creates a write-only string stream using the provided buffer for storage. -// The program aborts if writes would exceed the buffer's capacity. -export fn fixed(in: []u8) io::handle = { - let s = alloc(fixed_stream { +// The program aborts if writes would exceed the buffer's capacity. The stream +// doesn't need to be closed. +export fn fixed(in: []u8) fixed_stream = { + return fixed_stream { stream = io::stream { writer = &fixed_write, - closer = &fixed_close, ... }, buf = in, cur = in, - }); - return &s.stream; + }; }; // Returns the current contents of the buffer as a string. Aborts the program if // invalid UTF-8 has been written to the buffer. -export fn string(in: io::handle) str = { - let stream = match (in) { - case io::file => - abort("Invalid use of strio with io::file"); - case let st: *io::stream => - yield st; - }; - - if (stream.writer == &fixed_write) { - let stream = stream: *fixed_stream; - const n = len(stream.buf) - len(stream.cur); - return strings::fromutf8(stream.buf[..n]); - } else if (stream.writer == &dynamic_write) { - let stream = stream: *dynamic_stream; - return strings::fromutf8(stream.buf); - } else { - abort("strio::string called on non-strio stream"); +export fn string(in: (*fixed_stream | *dynamic_stream)) str = { + match (in) { + case let s: *fixed_stream => + const n = len(s.buf) - len(s.cur); + return strings::fromutf8(s.buf[..n]); + case let s: *dynamic_stream => + return strings::fromutf8(s.buf); }; }; @@ -55,15 +44,10 @@ fn fixed_write(s: *io::stream, buf: const []u8) (size | io::error) = { return n; }; -fn fixed_close(s: *io::stream) void = { - free(s); -}; - @test fn fixed() void = { static let buf: [1024]u8 = [0...]; let stream = fixed(buf); - defer io::close(stream); - io::write(stream, strings::toutf8("hello ")) as size; - io::write(stream, strings::toutf8("world")) as size; - assert(string(stream) == "hello world"); + io::write(&stream, strings::toutf8("hello ")) as size; + io::write(&stream, strings::toutf8("world")) as size; + assert(string(&stream) == "hello world"); }; diff --git a/strio/ops.ha b/strio/ops.ha @@ -21,10 +21,10 @@ export fn concat(out: io::handle, strs: str...) (size | io::error) = { @test fn concat() void = { let st = dynamic(); - defer io::close(st); - concat(st, "hello") as size; - concat(st, " ", "world") as size; - assert(string(st) == "hello world"); + defer io::close(&st); + concat(&st, "hello") as size; + concat(&st, " ", "world") as size; + assert(string(&st) == "hello world"); }; // Joins several strings together by a delimiter and writes them to a handle. @@ -55,15 +55,15 @@ export fn join(out: io::handle, delim: str, strs: str...) (size | io::error) = { @test fn join() void = { let st = dynamic(); - defer io::close(st); - join(st, "::", "hello", "world") as size; - assert(string(st) == "hello::world"); - truncate(st); - join(st, "::") as size; - assert(string(st) == ""); - truncate(st); - join(st, "::", "foo") as size; - assert(string(st) == "foo"); + defer io::close(&st); + join(&st, "::", "hello", "world") as size; + assert(string(&st) == "hello::world"); + truncate(&st); + join(&st, "::") as size; + assert(string(&st) == ""); + truncate(&st); + join(&st, "::", "foo") as size; + assert(string(&st) == "foo"); }; // Joins several strings together by a delimiter and writes them to a handle, in @@ -94,15 +94,15 @@ export fn rjoin(out: io::handle, delim: str, strs: str...) (size | io::error) = @test fn rjoin() void = { let st = dynamic(); - defer io::close(st); - rjoin(st, "::", "hello", "world") as size; - assert(string(st) == "world::hello"); - truncate(st); - rjoin(st, "::") as size; - assert(string(st) == ""); - truncate(st); - rjoin(st, "::", "foo") as size; - assert(string(st) == "foo"); + defer io::close(&st); + rjoin(&st, "::", "hello", "world") as size; + assert(string(&st) == "world::hello"); + truncate(&st); + rjoin(&st, "::") as size; + assert(string(&st) == ""); + truncate(&st); + rjoin(&st, "::", "foo") as size; + assert(string(&st) == "foo"); }; // Appends a rune to a stream. diff --git a/temp/+freebsd.ha b/temp/+freebsd.ha @@ -85,9 +85,8 @@ export fn dir() str = { random::buffer(buf[..]); let sink = strio::fixed(name); - defer io::close(sink); - hex::encode(sink, buf) as size; - let name = strio::string(sink); + hex::encode(&sink, buf) as size; + let name = strio::string(&sink); let path = path::join(get_tmpdir(), name); match (os::mkdir(path)) { diff --git a/temp/+linux.ha b/temp/+linux.ha @@ -99,9 +99,8 @@ export fn dir() str = { random::buffer(buf[..]); let sink = strio::fixed(name); - defer io::close(sink); - hex::encode(sink, buf) as size; - let name = strio::string(sink); + hex::encode(&sink, buf) as size; + let name = strio::string(&sink); let path = path::join(get_tmpdir(), name); match (os::mkdir(path)) { diff --git a/uuid/uuid.ha b/uuid/uuid.ha @@ -82,9 +82,8 @@ export fn uri(out: io::handle, in: uuid) (size | io::error) = { export fn encodestr(in: uuid) str = { static let buf: [UUID_STRLEN]u8 = [0...]; let sink = strio::fixed(buf); - defer io::close(sink); - encode(sink, in) as size; - return strio::string(sink); + encode(&sink, in) as size; + return strio::string(&sink); }; // Encodes a UUID as a string. The return value is statically allocated, the @@ -92,9 +91,8 @@ export fn encodestr(in: uuid) str = { export fn encodeuri(in: uuid) str = { static let buf: [UUID_URILEN]u8 = [0...]; let sink = strio::fixed(buf); - defer io::close(sink); - uri(sink, in) as size; - return strio::string(sink); + uri(&sink, in) as size; + return strio::string(&sink); }; @test fn encode() void = {