Soenneker.Utils.PooledStringBuilders
4.0.31
Prefix Reserved
dotnet add package Soenneker.Utils.PooledStringBuilders --version 4.0.31
NuGet\Install-Package Soenneker.Utils.PooledStringBuilders -Version 4.0.31
<PackageReference Include="Soenneker.Utils.PooledStringBuilders" Version="4.0.31" />
<PackageVersion Include="Soenneker.Utils.PooledStringBuilders" Version="4.0.31" />
<PackageReference Include="Soenneker.Utils.PooledStringBuilders" />
paket add Soenneker.Utils.PooledStringBuilders --version 4.0.31
#r "nuget: Soenneker.Utils.PooledStringBuilders, 4.0.31"
#:package Soenneker.Utils.PooledStringBuilders@4.0.31
#addin nuget:?package=Soenneker.Utils.PooledStringBuilders&version=4.0.31
#tool nuget:?package=Soenneker.Utils.PooledStringBuilders&version=4.0.31
Soenneker.Utils.PooledStringBuilders
Tiny, fast ref struct string builder.
Uses caller-provided storage or ArrayPool<char>. Low allocations. Short-lived use.
Installation
dotnet add package Soenneker.Utils.PooledStringBuilders
Example
using Soenneker.Utils.PooledStringBuilders;
using var sb = new PooledStringBuilder(128);
sb.Append("Hello, ");
sb.Append(name);
sb.Append(' ');
sb.Append(id); // ISpanFormattable path, no boxing
sb.AppendLine();
string s = sb.ToString(); // creates a string; using returns the buffer afterward
Use ToString() when the builder remains in scope and will be disposed separately. For a one-shot
finish without a using declaration:
var sb = new PooledStringBuilder();
sb.Append("value=");
sb.Append(value);
string result = sb.ToStringAndDispose();
Cheatsheet
new PooledStringBuilder(int capacity = 128)new PooledStringBuilder(Span<char> initialBuffer)— use stack or caller-owned memory until growth is neededAppend(char),Append(string?),Append(ReadOnlySpan<char>)Append<T>(T value, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)whereT : ISpanFormattableAppendSpan(int length)— reserve and write directly into the bufferInsert(...),Shrink(int),AppendLine(...),AppendSeparatorIfNotEmpty(char)Length,Capacity,AsSpan(),EnsureCapacity(int),Clear()ToString()— create a string without disposing the builderToStringAndDispose(bool clear = false)— create a string and return the bufferDispose()/Dispose(bool clear)
Notes
PooledStringBuilderis a stack-onlyref struct; it cannot be boxed, captured, stored in a normal field, or kept acrossawait.- Do not copy the builder (
var copy = builder). Copies refer to the same rented array and can return it to the pool more than once. Pass it byrefwhen a helper must mutate the same builder. - Dispose exactly once, either through
using,Dispose, orToStringAndDispose. Do not useToStringAndDisposeand then dispose a copied or aliased value. AppendSpan(length)immediately increasesLengthand returns uninitialized pooled storage. Fill the entire span before reading or converting the builder, or previous pool contents could appear in the result.AsSpan()is valid only until the builder grows, changes, or is disposed. Do not retain it.AppendLineappends\n, notEnvironment.NewLine.- Reading an empty default builder with
AsSpan(),ToString(), orToStringAndDispose()does not rent a buffer. A large first append rents its required capacity directly. - Integer appends reuse the remaining space when the value fits, even if the type's maximum width would not fit. Generic formatting uses all remaining buffer space before growing.
Append(ReadOnlySpan<char>)andAppendLine(ReadOnlySpan<char>)accept scoped spans, including temporary stack-allocated buffers, and copy their contents immediately.Clear()resets the logical length without zeroing storage.Dispose(clear: true)andToStringAndDispose(clear: true)clear the current storage, including caller-provided memory if it is still in use. Earlier storage released during growth is not cleared. The returned managed string is immutable.- The builder is not thread-safe. Keep it short-lived and confined to one synchronous scope.
Avoiding buffer rentals
For small results with a predictable upper bound, provide a modest stack buffer:
Span<char> initialBuffer = stackalloc char[128];
using var sb = new PooledStringBuilder(initialBuffer);
sb.Append("item=");
sb.Append(id);
ReadOnlySpan<char> contents = sb.AsSpan(); // consume before the builder is modified or disposed
This path does not rent or allocate a character buffer when the contents fit. If the contents outgrow
the supplied storage, the builder copies them into a pooled array. ToString() still creates the final
nonempty string; consume AsSpan() when a string is unnecessary. The initial storage must remain valid
for the builder's lifetime, and the caller retains ownership of it. Avoid large or repeated stack
allocations inside loops; allocate a modest initial buffer outside the loop and reuse it.
Benchmarks
The benchmark project compares rented and
stack-backed construction with direct span, TryFormat, and known-length string.Create controls.
Those controls have fewer responsibilities than a general-purpose builder and show the remaining overhead.
dotnet run --project benchmarks/Soenneker.Utils.PooledStringBuilders.Benchmarks -c Release -f net10.0 -- --filter "*" --runtimes net10.0
Use net8.0 or net9.0 for the other supported runtimes. Compare results on the same runtime and machine;
buffer sizes, formatting distributions, growth, and whether the result needs to be a string all affect performance.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (6)
Showing the top 5 NuGet packages that depend on Soenneker.Utils.PooledStringBuilders:
| Package | Downloads |
|---|---|
|
Soenneker.Extensions.String
A collection of useful string extension methods |
|
|
Soenneker.Extensions.Spans.ReadOnly.Strings
A collection of helpful ReadOnlySpan (string) extension methods |
|
|
Soenneker.Quark.Variables.Bootstrap
CSS variable overrides for the latest version of Bootstrap. |
|
|
Soenneker.Aws.Signing.V4
A dependency-free .NET implementation of AWS Signature Version 4 request signing. |
|
|
Soenneker.Hashing.Phc
A dependency-free .NET library for formatting and parsing Password Hashing Competition (PHC) strings. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.0.31 | 6,265 | 9/13/2026 |
| 4.0.29 | 301,373 | 8/30/2026 |
| 4.0.28 | 51,552 | 8/29/2026 |
| 4.0.27 | 412,933 | 7/27/2026 |
| 4.0.26 | 175,648 | 7/16/2026 |
| 4.0.25 | 260,022 | 6/18/2026 |
| 4.0.22 | 506,485 | 4/23/2026 |
| 4.0.21 | 244,946 | 3/14/2026 |
| 4.0.16 | 115,040 | 3/10/2026 |
| 4.0.15 | 56,687 | 3/9/2026 |
| 4.0.14 | 691 | 3/9/2026 |
| 4.0.13 | 91,986 | 3/4/2026 |
| 4.0.12 | 144,860 | 2/21/2026 |
| 4.0.11 | 5,008 | 2/21/2026 |
| 4.0.10 | 92,345 | 2/4/2026 |
| 4.0.9 | 94,677 | 1/8/2026 |
| 4.0.8 | 18,033 | 1/2/2026 |
| 4.0.7 | 37,024 | 11/20/2025 |
| 4.0.6 | 24,977 | 10/29/2025 |
| 3.0.5 | 3,391 | 10/23/2025 |
Optimize pooled string builder storage, formatting, and insertion