Something went wrong. Try again.
zig utility library forked from karlseguin.tngl.sh/zul
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314---layout: site.njktitle: zul.StringBuilder---
<h1 id=zul.StringBuilder><a href=#zul.StringBuilder aria-hidden=true>zul.StringBuilder</a></h1><p>Efficiently create/concat strings or binary data, optionally using a thread-safe pool with pre-allocated static buffers.</p>
<p>Applications that need to create many <code>StringBuilders</code> should consider using a <code><a href="#Pool">zul.StringBuilder.Pool</a></code>. <code>StringBuilders</code> created via the pool have a re-usable static buffer which can eliminate or reduce the need for dynamic memory allocations (assume the size of the static buffer is larger than the typical data written).</p>
<h2 id=example><a href=#example aria-hidden=true>Example</a></h2>{% highlight zig %}// StringBuilder can be used to efficiently concatenate strings// But it can also be used to craft binary payloads.var sb = zul.StringBuilder.init(allocator);defer sb.deinit();
// We're going to generate a 4-byte length-prefixed message.// We don't know the length yet, so we'll skip 4 bytes// We get back a "view" which will let us backfill the lengthvar view = try sb.skip(4);
// Writes a single bytetry sb.writeByte(10);
// Writes a []const u8try sb.write("hello");
// Using our view, which points to where the view was taken,// fill in the length.view.writeU32Big(@intCast(sb.len() - 4));
std.debug.print("{any}\n", .{sb.string()});// []u8{0, 0, 0, 6, 10, 'h', 'e', 'l', 'l', 'o'}{% endhighlight %}
<h2 id=StringBuilder><a href=#StringBuilder aria-hidden=true>StringBuilder struct</a></h2><p><code>zul.StringBuilder</code> is a wrapper around a dynamically growable <code>[]u8</code> and is designed for string concatenation or creating binary payloads. While <code>std.ArrayList(u8)</code> can be used for the same purpose, the non-generic nature of <code>StringBuilder</code> results in a cleaner interface.</p>
<h3 id=StringBuilder.fields><a href=#StringBuilder.fields aria-hidden=true>Fields</a></h3><div> <div class=member> <code class=name>endian: std.builtin.Endian</code> <div class=desc> <p>The endianness to use, <code>.big</code> or <code>.little</code>, when encoding integers. Defaults to the current architecture.</p> </div> </div></div>
<h3 id=StringBuilder.methods><a href=#StringBuilder.methods aria-hidden=true>Methods</a></h3><div> <div class=member> <code class=name id=StringBuilder.init><a href=#StringBuilder.init aria-hidden=true>init(allocator: Allocator) StringBuilder</a></code> <div class=desc> <p>Creates a <code>StringBuilder</code>.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.deinit><a href=#StringBuilder.deinit aria-hidden=true>deinit(self: *const StringBuilder) void</a></code> <div class=desc> <p>Releases all allocated memory. The <code>StringBuilder</code> should not be used after this is called.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.clearRetainingCapacity><a href=#StringBuilder.clearRetainingCapacity aria-hidden=true>clearRetainingCapacity(self: StringBuilder) void</a></code> <div class=desc> <p>Resets the <code>StringBuilder</code>'s length to 0 without freeing the underlying buffer. It is safe to re-use the <code>StringBuilder</code>.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.copy><a href=#StringBuilder.copy aria-hidden=true>copy(sb: StringBuilder, allocator: Allocator) ![]u8</a></code> <div class=desc> <p>Creates a copy of the written data using the provided <code>allocator</code>.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.ensureUnusedCapacity><a href=#StringBuilder.ensureUnusedCapacity aria-hidden=true>ensureUnusedCapacity(sb: *StringBuilder, n: usize) !void</a></code> <div class=desc> <p>Ensures that the the underlying buffer has <code>n</code> spare bytes. If not, tries to allocates enough memory to allocate <code>n</code> additional bytes.</p> <p>This can be used to pre-grow the buffer prior to making a large number of writes.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.fromOwnedSlice><a href=#StringBuilder.fromOwnedSlice aria-hidden=true>fromOwnedSlice(...) StringBuilder</a></code>{% highlight zig %}fn fromOwnedSlice( // When deinit is called on the returned StringBuilder, buf will be freed // with this allocator. Thus, buf must have been created with this // allocator. allocator: std.mem.Allocator,
// The position of the returned StringBuilder will be set to buf.len (thus // any subsequent writes will be appended) buf: []u8,
) StringBuilder{% endhighlight %}
<div class=desc> <p>Creates a StringBuilder around the provided <code>buf</code>. <code>buf</code> must have been created by the provided <code>allocator</code>.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.fromReader><a href=#StringBuilder.fromReader aria-hidden=true>fromReader(...) !StringBuilder</a></code>{% highlight zig %}fn fromReader( allocator: std.mem.Allocator,
// Typically an std.Io.Reader, but can be any type that has a read([]u8) !size method reader: anytype,
// {.max_size = usize, .buffer_size = usize} opts: FromReaderOpts
) !StringBuilder{% endhighlight %} <div class=desc> <p>Creates and populates a <code>StringBuilder</code> by reading from the <code>reader</code>.</p>
<p>Options:</p> <ul> <li><code>max_size: usize</code> - Will return an <code>error.TooBig</code> if more than the specified value is read. Default: <code>std.math.maxInt(usize)</code>. <li><code>buffer_size: usize</code> - For the sake of efficiency, the reader is read directly into the StringBuilder's underlying buffer (there is no intermediate buffer). `buffer_size` controls the (a) initial size and (b) growth of the underlying buffer. Defaults to `8192`. Is forced to <code>64</code> when the value is less than <code>64</code> </ul> </div> </div> <div class=member> <code class=name id=StringBuilder.len><a href=#StringBuilder.len aria-hidden=true>len(sb: StringBuilder) usize</a></code> <div class=desc> <p>Returns the length of the written data.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.release><a href=#StringBuilder.release aria-hidden=true>release(self: *StringBuilder) void</a></code> <div class=desc> <p>Releases the <code>StringBuilder</code> back to the pool. This method is only valid when the <code>StringBuilder</code> was retrieved from <code>pool.acquire()</code>. The <code>StringBuilder</code> should not be used after this is called.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.skip><a href=#StringBuilder.skip aria-hidden=true>skip(sb: *StringBuilder, count: usize) !View</a></code> <div class=desc> <p>Skips the specified number of bytes and returns a <code><a href="#View">View</a></code> that can be used to backfill the skipped data.</p> <p><code>count</code> can be larger than the existing buffer length, in which case <code>skip</code> will attempt to grow the buffer.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.string><a href=#StringBuilder.string aria-hidden=true>string(sb: StringBuilder) []u8</a></code> <div class=desc> <p>Returns the bytes written. Subsequent modifications to the <code>StringBuilder</code> may invalidate the returned data</p> </div> </div> <div class=member> <code class=name id=StringBuilder.stringZ><a href=#StringBuilder.stringZ aria-hidden=true>stringZ(sb: StringBuilder) ![:0]u8</a></code> <div class=desc> <p>Returns the bytes written as a null-terminated string. Subsequent modifications to the <code>StringBuilder</code> may invalidate the returned data</p> </div> </div> <div class=member> <code class=name id=StringBuilder.truncate><a href=#StringBuilder.truncate aria-hidden=true>truncate(sb: *StringBuilder, n: usize) void</a></code> <div class=desc> <p>Truncates the data by <code>n</code> bytes. If <code>n</code> is greater than the length of the buffer, the length is set to 0. Does not free memory.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.write><a href=#StringBuilder.write aria-hidden=true>write(sb: *StringBuilder, data: []u8) !void</a></code> <div class=desc> <p>Appends <code>data</code>, growing the underlying buffer if needed</p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeAssumeCapacity><a href=#StringBuilder.writeAssumeCapacity aria-hidden=true>writeAssumeCapacity(sb: *StringBuilder, data: []u8) void</a></code> <div class=desc> <p>Appends <code>data</code> assuming that the underlying buffer has enough free space. This method is slightly faster than <code>write</code> but is unsafe and should only be used in conjunction with <code><a href="#StringBuilder.ensureUnusedCapacity">ensureUnusedCapacity</a></code>.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeByte><a href=#StringBuilder.writeByte aria-hidden=true>writeByte(sb: *StringBuilder, b: u8) !void</a></code> <div class=desc> <p>Appends the byte, growing the underlying buffer if needed</p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeByteAssumeCapacity><a href=#StringBuilder.writeByteAssumeCapacity aria-hidden=true>writeByteAssumeCapacity(sb: *StringBuilder, b: u8) void</a></code> <div class=desc> <p>Appends the byte assuming that the underlying buffer has enough free space. This method is slightly faster than <code>writeByte</code> but is unsafe and should only be used in conjunction with <code><a href="#StringBuilder.ensureUnusedCapacity">ensureUnusedCapacity</a></code>.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeByteNTimes><a href=#StringBuilder.writeByteNTimes aria-hidden=true>writeByteNTimes(sb: *StringBuilder, b: u8, n: usize) !void</a></code> <div class=desc> <p>Appends the byte <code>n</code> times, growing the underlying buffer if needed.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.writer><a href=#StringBuilder.writer aria-hidden=true>writer(sb: *StringBuilder) std.Io.Writer</a></code> <div class=desc> <p>Returns an <code>std.Io.Writer</code>. This allows the <code>StringBuilder</code> to receive writes from a number of <code>std</code> and third party libraries, such as <code>std.json.stringify</code>.</p> </div> </div></div>
<h3 id=StringBuilder.int.methods><a href=#StringBuilder.wrinteInt.methods aria-hidden=true>WriteInt Methods</a></h3><p>The <code>StringBuilder</code> exposes a variety of methods for writing integers. There's overlap between some of these functions in order to accommodate various styles/preferences.</p>
<div> <div class=member> <code class=name id=StringBuilder.writeU16><a href=#StringBuilder.writeU16 aria-hidden=true>writeU16(sb: *StringBuilder, value u16) !void</a></code> <div class=desc> <p>Writes an unsigned integer using the encoding defined by <code>sb.endian</code>.</p> <p>Variants: <code>writeU32</code> and <code>writeU64</code></p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeI16><a href=#StringBuilder.writeI16 aria-hidden=true>writeI16(sb: *StringBuilder, value i16) !void</a></code> <div class=desc> <p>Writes an signed integer using the encoding defined by <code>sb.endian</code>.</p> <p>Variants: <code>writeI32</code> and <code>writeI64</code></p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeU16Little><a href=#StringBuilder.writeU16Little aria-hidden=true>writeU16Little(sb: *StringBuilder, value u16) !void</a></code> <div class=desc> <p>Writes an unsigned integer using little endian encoding.</p> <p>Variants: <code>writeU32Little</code> and <code>writeU64Little</code></p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeI16Little><a href=#StringBuilder.writeI16Little aria-hidden=true>writeI16Little(sb: *StringBuilder, value u16) !void</a></code> <div class=desc> <p>Writes an signed integer using little endian encoding.</p> <p>Variants: <code>writeI32Little</code> and <code>writeI64Little</code></p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeU16Big><a href=#StringBuilder.writeU16Big aria-hidden=true>writeU16Big(sb: *StringBuilder, value u16) !void</a></code> <div class=desc> <p>Writes an unsigned integer using big endian encoding.</p> <p>Variants: <code>writeU32Big</code> and <code>writeU64Big</code></p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeI16Big><a href=#StringBuilder.writeI16Big aria-hidden=true>writeI16Big(sb: *StringBuilder, value u16) !void</a></code> <div class=desc> <p>Writes an signed integer using big endian encoding.</p> <p>Variants: <code>writeI32Big</code> and <code>writeI64Big</code></p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeInt><a href=#StringBuilder.writeInt aria-hidden=true>writeInt(sb: *StringBuilder, value anytype) !void</a></code> <div class=desc> <p>Writes an integer using the encoding defined by <code>sb.endian</code>. Type type of <code>value</code> determines how the value is encoded.</p> <p>It is a compile-time error to pass a <code>comptime_int</code>. You must cast the constant.</p> </div> </div> <div class=member> <code class=name id=StringBuilder.writeIntAs><a href=#StringBuilder.writeIntAs aria-hidden=true>writeIntAs(sb: *StringBuilder, value anytype, endian: Endian) !void</a></code> <div class=desc> <p>Writes an integer using the specified endianness. Type type of <code>value</code> determines how the value is encoded.</p> <p>It is a compile-time error to pass a <code>comptime_int</code>. You must cast the constant.</p> </div> </div></div>
<h2 id=View><a href=#View aria-hidden=true>View struct</a></h2><p>When the <code><a href=#StringBuilder.skip>sb.skip()</a></code> method is called, a <code>View</code> is returned to backfill the skipped space. This is API exists to "reserve" space within the buffer for data unknown until a later point. The typical example is a length-prefixed message where the length is only known after the message has been written.</p>
<p>The <code>View</code> exposes all of the same <code>write*</code> methods as the <code>StringBuilder</code>, but will not grow the buffer and <strong>does no bound-checking</strong>.</p>
<h2 id=Pool><a href=#Pool aria-hidden=true>Pool struct</a></h2><p><code>zul.StringBuilder.Pool</code> is a thread-safe pool of <code>zul.StringBuilders</code> that will grow as needed. Each <code>zul.StringBuilder</code> has a re-usable static buffer which will be used before any dynamic allocations are needed.</p>
{% highlight zig %}// create a pool of 10 StringBuilders// with each StringBuilder having a re-usable static buffer of 1024 bytes.// pool is thread-safevar pool = zul.StringBuilder.Pool.init(t.allocator, 10, 1024);defer pool.deinit();
var sb = try pool.acquire();defer sb.release();
// Use sb as documented abovesb.write("hello world");...{% endhighlight %}
<h3 id=Pool.methods><a href=#Pool.methods aria-hidden=true>Methods</a></h3><div> <div class=member> <code class=name id=Pool.init><a href=#Pool.init aria-hidden=true>init(allocator: Allocator, pool_size: u16, static_size: usize) Pool</a></code> <div class=desc> <p>Creates a <code>StringBuilder.Pool</code>. The pool will have <code>pool_size</code> <code>StringBuilders</code>, and each <code>StringBuilder</code> will maintain a static buffer of <code>static_size</code> bytes.</p> </div> </div> <div class=member> <code class=name id=Pool.deinit><a href=#Pool.deinit aria-hidden=true>deinit(self: *Pool) void</a></code> <div class=desc> <p>Releases all allocated memory. The <code>Pool</code> and any of its <code>StringBuilders</code> should not be used after this is called.</p> </div> </div> <div class=member> <code class=name id=Pool.acquire><a href=#Pool.acquire aria-hidden=true>acquire(self: *Pool) ?*StringBuilder</a></code> <div class=desc> <p>Retrieves a <code>StringBuilder</code> from the pool. If the pool is empty, will attempt to create a new one. This method is thread-safe.</p> </div> </div> <div class=member> <code class=name id=Pool.acquire><a href=#Pool.acquire aria-hidden=true>release(self: *Pool, sb: *StringBuilder) void</a></code> <div class=desc> <p>Releases the <code>StringBuilder</code> back into the pool. If the pool is full, the <code>StringBuilder</code> is freed. This method is thread-safe. Most implentations will prefer to call <code>release()</code> on the <code>StringBuilder</code> directly.</p> </div> </div></div>