ZaString
ZaString is a high-performance, zero-allocation string manipulation library for C# that uses Span<T> and ReadOnlySpan<T> for optimal memory efficiency. Built for .NET 9.0+, it provides a fluent API for building strings without heap allocations.
Install / Use
/learn @CorentinGS/ZaStringREADME
ZaString

ZaString is a high-performance, zero-allocation string toolbox for modern .NET applications. It provides stack-first, Span-based builders and helpers so you can assemble, format and encode strings with predictable, allocation-free performance.
🚀 Why ZaString?
ZaString is built for scenarios where every allocation matters. If you're writing high-frequency, low-latency services, middleware, or hotspots that produce a lot of transient strings (logging, serialization, templating), ZaString helps keep GC pressure low while staying familiar and idiomatic to C# developers.
Key highlights:
- Zero allocation: Stack-first, Span-backed APIs avoid ephemeral heap allocations
- High performance: Faster than standard
StringBuilderin many scenarios (see benchmarks) - Fluent ergonomic API: Chainable methods for simple building and complex formatting
- UTF‑8 support: Write bytes directly when you need to emit UTF‑8
- Escape helpers: Ready-to-use JSON, HTML, CSV, URL escaping utilities
- Interpolated string handlers: Integrates with C# interpolation for zero-cost formatting
📦 Installation
Install from NuGet:
dotnet add package ZaString
🎯 Quick start
using ZaString.Core;
using ZaString.Extensions;
// Create a stack-allocated buffer and a builder
Span<char> buffer = stackalloc char[100];
var builder = ZaSpanStringBuilder.Create(buffer);
// Build strings with a fluent, zero-allocation API
builder.Append("Hello, ")
.Append("World!")
.Append(" Number: ")
.Append(42)
.Append(" Pi: ")
.Append(Math.PI, "F2");
var spanResult = builder.AsSpan(); // zero-allocation read-only span
Console.WriteLine(spanResult.ToString()); // prints: Hello, World! Number: 42 Pi: 3.14
🔧 Core components
ZaSpanStringBuilder (stack-first builder)
The main string builder that writes directly to a provided Span<char>:
Span<char> buffer = stackalloc char[64];
var builder = ZaSpanStringBuilder.Create(buffer);
builder.Append("User: ")
.Append("John Doe")
.Append(", Age: ")
.Append(25)
.Append(", Active: ")
.Append(true);
// Access as ReadOnlySpan<char> (zero allocation)
var userInfo = builder.AsSpan();
ZaPooledStringBuilder (pooled/heap-backed builder)
For scenarios requiring heap allocation with automatic buffer management:
using var builder = ZaPooledStringBuilder.Rent(128);
builder.Append("Pooled string building")
.Append(" with automatic cleanup");
var result = builder.ToString();
// Buffer automatically returned to pool when disposed
ZaUtf8SpanWriter
UTF-8 byte-level string writing:
Span<byte> buffer = stackalloc byte[256];
var writer = ZaUtf8SpanWriter.Create(buffer);
writer.Append("Hello, UTF-8 World!")
.Append(" Number: ")
.Append(123);
var utf8Bytes = writer.AsSpan();
🎨 Advanced features
String Interpolation
var name = "Alice";
var age = 30;
var pi = Math.PI;
builder.Append($"User: {name}, Age: {age}, Pi: {pi:F2}");
Number Formatting
builder.Append("Currency: ")
.Append(1234.56, "C") // "$1,234.56"
.Append(", Number: ")
.Append(12345, "N0") // "12,345"
.Append(", Percentage: ")
.Append(0.85, "P2"); // "85.00%"
Culture-Specific Formatting
var fr = new CultureInfo("fr-FR");
builder.Append(1234.56, "C", fr); // "1 234,56 €"
Conditional Appending
var isActive = true;
builder.Append("Status: ")
.AppendIf(isActive, "Active", "Inactive");
Escape Helpers
// JSON escaping
builder.AppendJsonEscaped("Line1\nLine2\t\"Quote\"");
// HTML escaping
builder.AppendHtmlEscaped("<script>alert('xss')</script>");
// URL encoding
builder.AppendUrlEncoded("Hello World!");
// CSV escaping
builder.AppendCsvEscaped("Value,with,commas");
URL Building
builder.AppendPathSegment("api")
.AppendPathSegment("v1")
.AppendPathSegment("users")
.AppendQueryParam("q", "search term", isFirst: true)
.AppendQueryParam("page", "1");
// Result: "api/v1/users?q=search%20term&page=1"
TryAppend Methods
Non-throwing variants for buffer overflow handling:
Span<char> smallBuffer = stackalloc char[10];
var builder = ZaSpanStringBuilder.Create(smallBuffer);
if (builder.TryAppend("Hello, World!"))
{
Console.WriteLine("Successfully appended");
}
else
{
Console.WriteLine("Buffer too small");
}
📊 Performance
ZaString significantly outperforms traditional string-building approaches. See the benchmark results below and in
the tests/ZaString.Benchmarks project for details.
Basic String Building Performance
| Method | Mean Time | Memory Allocations | Performance Ratio |
|----------------------------|--------------|--------------------|-------------------|
| StringBuilder (Baseline) | 146.1 ns | 480 B | 1.00x |
| String concatenation | 116.3 ns | 248 B | 0.80x |
| String interpolation | 116.9 ns | 136 B | 0.80x |
| ZaSpanStringBuilder | 115.2 ns | 0 B | 0.79x |
Detailed Benchmark Results
Basic String Building:
StringBuilder: 146.1 ns, 480 B allocatedStringConcatenation: 116.3 ns, 248 B allocatedStringInterpolation: 116.9 ns, 136 B allocated- ZaSpanStringBuilder: 115.2 ns, 0 B allocated ⚡
Number Formatting:
StringBuilder: 295.3 ns, 584 B allocated- ZaSpanStringBuilder: 234.9 ns, 0 B allocated (20% faster)
Large String Operations:
StringBuilder: 1,565.9 ns, 27,312 B allocated- ZaSpanStringBuilder: 1,236.5 ns, 0 B allocated (21% faster)
DateTime Formatting:
StringBuilder: 189.0 ns, 384 B allocated- ZaSpanStringBuilder: 135.7 ns, 0 B allocated (28% faster)
Span vs String Processing:
StringBuilder: 24.7 ns, 256 B allocated- ZaSpanStringBuilder: 10.4 ns, 0 B allocated (58% faster)
Number Formatting Performance using Builder
These results use a builder baseline (StringBuilder.AppendFormat with InvariantCulture) for apples-to-apples comparison against ZaSpanStringBuilder (zero allocation).
| Case | Builder Mean | Builder Alloc | ZaSpan Mean | ZaSpan Alloc | |------------------------|-------------:|--------------:|------------:|-------------:| | Double | 128.70 ns | 176 B | 104.26 ns | 0 B | | Double (Formatted F2) | 94.20 ns | 160 B | 73.33 ns | 0 B | | Float | 105.12 ns | 168 B | 88.40 ns | 0 B | | Long | 27.51 ns | 176 B | 12.58 ns | 0 B | | Integer (Formatted N0) | 59.43 ns | 168 B | 38.28 ns | 0 B |
Environment: .NET 8.0.19, Ryzen 9 5950X, BenchmarkDotNet 0.15.2.
Key Performance Benefits
- Zero Memory Allocations: ZaSpanStringBuilder uses stack-allocated buffers
- 20-58% Faster: Significantly outperforms StringBuilder across most scenarios
- Predictable Performance: No GC pressure or memory fragmentation
- Scalable: Performance scales linearly with string size
- Memory Efficient: Up to 100% reduction in memory allocations
Benchmark Example
// Traditional approach - 146.1 ns, 480 B allocated
var sb = new StringBuilder();
sb.Append("Name: ").Append("John").Append(", Age: ").Append(25);
var result = sb.ToString();
// ZaString approach - 115.2 ns, 0 B allocated
Span<char> buffer = stackalloc char[50];
var builder = ZaSpanStringBuilder.Create(buffer);
builder.Append("Name: ").Append("John").Append(", Age: ").Append(25);
var result = builder.AsSpan(); // Zero allocation
🛠️ Use Cases
- High-frequency string operations in performance-critical applications
- Parsing and formatting without memory pressure
- HTTP response building in web servers
- Logging and diagnostics with minimal overhead
- Data serialization to avoid temporary allocations
- Real-time applications requiring predictable performance
🔍 API Reference
Core Methods
Create(Span<char>)- Create a new builder with a bufferAppend()- Append various types with fluent APIAppendLine()- Append with line terminatorTryAppend()- Non-throwing append variantsAsSpan()- Get result asReadOnlySpan<char>ToString()- Get result as string (allocates)Clear()- Reset builder for reuseSetLength(int)- Set current lengthRemoveLast(int)- Remove characters from end
Extension Methods
AppendIf()- Conditional appendingAppendJoin()- Join collections with separatorsAppendRepeat()- Repeat charactersAppendFormat()- Composite formattingAppendJsonEscaped()- JSON string escapingAppendHtmlEscaped()- HTML entity escapingAppendUrlEncoded()- URL percent encodingAppendCsvEscaped()- CSV field escapingAppendPathSegment()- URL path buildingAppendQueryParam()- URL query parameter building
🛡️ Best Practices & Advanced Usage
Ref Struct Limitations
ZaSpanStringBuilder and ZaUtf8SpanWriter are ref struct types. This means they:
- Cannot be boxed (cast to
objector interface). - Cannot be fields of a class (only other
ref structs). - Cannot be used in
asyncmethods acrossawaitpoints. - Are designed for short-lived, stack-based operations.
