builder
Package builder.
Assembling a result one piece at a time with + is quadratic: a + b allocates and copies both sides, so the cost is the sum of every intermediate length. Building 80 KB one byte at a time takes about two seconds, and a megabyte would take minutes. builder collects the pieces and copies each once, at the end.
import "builder";
let b = builder.new_str();
b.write("hello").write(", ").write(name).write_int(42).write_line("!");
let s = b.finish(); // one allocation, linear in the total
let y = builder.new_bytes();
y.write_byte(104).write(b"ello").write_str(" world");
let raw: bytes = y.finish();Str is for text and Bytes for binary data. Both chain, report size() in bytes without assembling anything, and offer finish() (which can be called again after more writes), take() (finish and start over) and reset(). The chunk is made on the first short write, so a builder that only takes long pieces never pays for it, and take() hands back a lone piece without copying it. Bytes.write_byte fills a 512-byte chunk in place, so a million single-byte writes make about two thousand allocations rather than a million: 4 million of them take 136 ms. Bytes keeps a copy of what you write, because bytes are mutable and a later change of yours must not rewrite what was already written.
For assembling a [bytes] you already hold, strings.join_bytes(parts, sep) is the counterpart of strings.join. bench/builder/ prints the naive loop beside the builder.
API#
Appends, and returns the builder so writes chain. A str is immutable, so the builder holds the caller's value, not a copy.
The bytes written so far, without assembling anything.
The whole result, in one allocation. Can be called again after more writes; each call assembles what is there, so ask once when you are done rather than after every write.
finish() and start over.
The chunk starts as the shared empty b"" and is made on the first short write (has_chunk): a builder that only takes long pieces, or none, never pays for it. It used to be built eagerly as a 512-byte str and then copied to bytes -- 1 KB and two allocations before any write.
Appends a copy. Bytes are mutable in slang, so keeping the caller's own value would let a later change of theirs rewrite what was already written.
Text goes in as its UTF-8 bytes. to_bytes already makes a fresh value, so a long string is kept as-is rather than copied twice.
The whole result, in one allocation. Leaves the builder as it was, so it can be written to and finished again; call it once when you are done rather than after every write.
finish, then reset. With a single piece that piece is the result: the reset drops the builder's hold on it, so no copy is needed.