Skip to content

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.

Notifications You must be signed in to change notification settings

CorentinGS/ZaString

Repository files navigation

ZaString

ZaString logo

.NET NuGet License

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 StringBuilder in 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 allocated
  • StringConcatenation: 116.3 ns, 248 B allocated
  • StringInterpolation: 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 buffer
  • Append() - Append various types with fluent API
  • AppendLine() - Append with line terminator
  • TryAppend() - Non-throwing append variants
  • AsSpan() - Get result as ReadOnlySpan<char>
  • ToString() - Get result as string (allocates)
  • Clear() - Reset builder for reuse
  • SetLength(int) - Set current length
  • RemoveLast(int) - Remove characters from end

Extension Methods

  • AppendIf() - Conditional appending
  • AppendJoin() - Join collections with separators
  • AppendRepeat() - Repeat characters
  • AppendFormat() - Composite formatting
  • AppendJsonEscaped() - JSON string escaping
  • AppendHtmlEscaped() - HTML entity escaping
  • AppendUrlEncoded() - URL percent encoding
  • AppendCsvEscaped() - CSV field escaping
  • AppendPathSegment() - URL path building
  • AppendQueryParam() - 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 object or interface).
  • Cannot be fields of a class (only other ref structs).
  • Cannot be used in async methods across await points.
  • Are designed for short-lived, stack-based operations.

Disposal & Pooling

  • ZaPooledStringBuilder: Always use using or call Dispose() to return the internal buffer to the ArrayPool. Failure to do so will lead to memory leaks in the pool.
  • ZaUtf8Handle: This struct wraps a pooled array. You MUST call Dispose() when finished. Avoid copying this struct around, as multiple disposals of the same handle can corrupt the pool.

Unsafe Pointers

ZaUtf8Handle.Pointer provides raw access to the underlying buffer.

  • Warning: The underlying array is NOT pinned by default.
  • If you need a stable pointer for external calls (P/Invoke) or async operations, you must pin the array manually (e.g., using fixed or GCHandle).
  • Accessing the pointer after disposal is undefined behavior.

πŸ§ͺ Testing & benchmarks

Run the full unit tests:

dotnet test

Run performance benchmarks (Release):

dotnet run --project tests/ZaString.Benchmarks -c Release

πŸ“ Examples

See the samples directory for complete working examples demonstrating all features.

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Built with modern C# features and .NET 9.0
  • Inspired by the performance benefits of Span<T> and Memory<T>
  • Designed for zero-allocation scenarios in high-performance applications

Made with ❀️ for high-performance .NET applications

About

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.

Resources

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages