Bia.ValueBuffers 0.9.0

dotnet add package Bia.ValueBuffers --version 0.9.0
                    
NuGet\Install-Package Bia.ValueBuffers -Version 0.9.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bia.ValueBuffers" Version="0.9.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bia.ValueBuffers" Version="0.9.0" />
                    
Directory.Packages.props
<PackageReference Include="Bia.ValueBuffers" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bia.ValueBuffers --version 0.9.0
                    
#r "nuget: Bia.ValueBuffers, 0.9.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bia.ValueBuffers@0.9.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bia.ValueBuffers&version=0.9.0
                    
Install as a Cake Addin
#tool nuget:?package=Bia.ValueBuffers&version=0.9.0
                    
Install as a Cake Tool

Bia.ValueBuffers

.NET C# Build Status codecov Nuget License

High-performance, stack-first value-type buffers for .NET. Zero-allocation where possible, ArrayPool-backed overflow. Cross-platform, trimmable and AOT/NativeAOT compatible.

⭐ Please star this project if you like it. ⭐

Example | ValueStringBuilder | ValueRingBuffer | ValueByteBuffer | ValueTokenizer | Public API

Example

// ValueStringBuilder — stack-first string building, zero allocation on the happy path
Span<char> buf = stackalloc char[128];
var sb = new ValueStringBuilder(buf);
sb.Append("Count: ");
sb.Append(42); // formats int directly, no alloc
sb.Append(", pi: ");
sb.Append(3.14159, "F4"); // formats double with format string
string message = sb.ToString(); // materializes string and returns pooled memory

// ValueRingBuffer<T> — fixed-capacity circular buffer backed by stackalloc
Span<int> ring = stackalloc int[4];
var rb = new ValueRingBuffer<int>(ring);
rb.Write(1);
rb.Write(2);
rb.Write(3);
rb.Write(4);
rb.Write(5); // buffer is full — overwrites oldest element (1)
int oldest = rb.Read(); // 2 (FIFO)

// ValueByteBuffer — stack-first binary data assembly
Span<byte> bytes = stackalloc byte[16];
using var bb = new ValueByteBuffer(bytes);
bb.WriteUInt32LittleEndian(0xDEADBEEF);
bb.Write((byte)0xFF);
byte[] packet = bb.ToArray(); // snapshot to heap; bb is disposed at end of scope

// ValueTokenizer — zero-allocation span-based tokenizer
var tokenizer = new ValueTokenizer("{one}{two}{three}".AsSpan());
while (tokenizer.TryReadNext(out ReadOnlySpan<char> token))
    _ = token; // process token as span, no heap allocation

_ = message;
_ = oldest;
_ = packet;

For more examples see Example Catalogue.

Benchmarks

Run dotnet Build.cs comparison-bench then dotnet test to populate results locally.

Detailed Benchmarks

Comparison Benchmarks
TestBench Benchmark Results

Example Catalogue

The following examples are available in ReadMeTest.cs.

Example - Overview

// ValueStringBuilder — stack-first string building, zero allocation on the happy path
Span<char> buf = stackalloc char[128];
var sb = new ValueStringBuilder(buf);
sb.Append("Count: ");
sb.Append(42); // formats int directly, no alloc
sb.Append(", pi: ");
sb.Append(3.14159, "F4"); // formats double with format string
string message = sb.ToString(); // materializes string and returns pooled memory

// ValueRingBuffer<T> — fixed-capacity circular buffer backed by stackalloc
Span<int> ring = stackalloc int[4];
var rb = new ValueRingBuffer<int>(ring);
rb.Write(1);
rb.Write(2);
rb.Write(3);
rb.Write(4);
rb.Write(5); // buffer is full — overwrites oldest element (1)
int oldest = rb.Read(); // 2 (FIFO)

// ValueByteBuffer — stack-first binary data assembly
Span<byte> bytes = stackalloc byte[16];
using var bb = new ValueByteBuffer(bytes);
bb.WriteUInt32LittleEndian(0xDEADBEEF);
bb.Write((byte)0xFF);
byte[] packet = bb.ToArray(); // snapshot to heap; bb is disposed at end of scope

// ValueTokenizer — zero-allocation span-based tokenizer
var tokenizer = new ValueTokenizer("{one}{two}{three}".AsSpan());
while (tokenizer.TryReadNext(out ReadOnlySpan<char> token))
    _ = token; // process token as span, no heap allocation

_ = message;
_ = oldest;
_ = packet;

Example - ValueStringBuilder

// Append text, numbers, and formatted values — all without intermediate allocations
Span<char> buf = stackalloc char[128];
var sb = new ValueStringBuilder(buf);
sb.Append("Count: ");
sb.Append(42); // int — no boxing, no ToString allocation
sb.Append(", ratio: ");
sb.Append(0.5, "P0"); // double with format string
sb.AppendLine();
sb.Append("Guid: ");
sb.Append(Guid.Empty); // any ISpanFormattable, zero alloc
string result = sb.ToString(); // materializes and disposes

// WrittenSpan: inspect built content without allocating a string
Span<char> buf2 = stackalloc char[64];
var sb2 = new ValueStringBuilder(buf2);
sb2.Append("hello");
sb2.Append(' ');
sb2.Append("world");
ReadOnlySpan<char> view = sb2.WrittenSpan; // zero-alloc view of "hello world"
_ = view; // pass to Encoding, TextWriter, comparers — no string needed
sb2.Dispose();

// AppendEnclosedJoin: format span-backed numeric sequences without intermediate strings
var items = new[] { 1, 2, 3 };
Span<char> buf3 = stackalloc char[64];
var sb3 = new ValueStringBuilder(buf3);
sb3.AppendEnclosedJoin("[".AsSpan(), ", ".AsSpan(), "]".AsSpan(), items, "D2");
string joined = sb3.ToString(); // "[01, 02, 03]"

// IEnumerable<T> enclosed joins can now stay bounded without dropping to manual loops
IEnumerable<int> ids = [7, 42, 99, 120];
Span<char> buf4 = stackalloc char[64];
var sb4 = new ValueStringBuilder(buf4);
sb4.AppendEnclosedJoin(
    "[".AsSpan(),
    ", ".AsSpan(),
    "]".AsSpan(),
    ids,
    maxItems: 3,
    overflowSuffix: " more".AsSpan()
);
string boundedIds = sb4.ToString(); // "[7, 42, 99, +1 more]"

// AppendPadded keeps fixed-width columns readable without interpolation fallback
Span<char> buf5 = stackalloc char[64];
var sb5 = new ValueStringBuilder(buf5);
sb5.AppendPadded("slot".AsSpan(), 8);
sb5.AppendPadded(12, 4, leftAlign: false, padChar: '0');
string padded = sb5.ToString(); // "slot    0012"

// Standard interpolation alignment now works directly on the builder handler
Span<char> buf6 = stackalloc char[64];
var sb6 = new ValueStringBuilder(buf6);
string label = "slot";
int count = 12;
sb6.Append($"[{label, 6}] {count, 4:D2}");
string aligned = sb6.ToString(); // "[  slot]   12"

// Formatter structs are still the better fit when each item needs custom layout
byte[] payload = [0xDE, 0xAD, 0xBE, 0xEF];
Span<char> buf7 = stackalloc char[32];
var sb7 = new ValueStringBuilder(buf7);
sb7.AppendJoin(" | ".AsSpan(), items, new SlotFormatter());
string formattedSlots = sb7.ToString(); // "#01 | #02 | #03"

// Span-based hex helpers now support prefixed byte dumps directly on the builder
Span<char> buf8 = stackalloc char[32];
var sb8 = new ValueStringBuilder(buf8);
sb8.AppendHex(payload, "0x".AsSpan(), "-".AsSpan());
string hex = sb8.ToString(); // "0xDE-AD-BE-EF"

// WriteEncodedTo: bridge staged text directly into a caller-owned byte buffer
Span<char> buf9 = stackalloc char[32];
var sb9 = new ValueStringBuilder(buf9);
sb9.Append("Caf\u00E9");
Span<byte> bytes = stackalloc byte[16];
var bb = new ValueByteBuffer(bytes);
sb9.WriteEncodedTo(Encoding.UTF8, ref bb);
ReadOnlySpan<byte> utf8 = bb.WrittenSpan; // [43 61 66 C3 A9]

_ = result;
_ = joined;
_ = boundedIds;
_ = padded;
_ = aligned;
_ = formattedSlots;
_ = hex;
_ = utf8;

bb.Dispose();
sb9.Dispose();

Use the IEnumerable<T> join overloads when the source sequence shape is the only obstacle and each element already implements ISpanFormattable. Use a formatter struct when each element needs extra punctuation, labels, or mixed formatting logic.

Example - ValueRingBuffer

// Circular overwrite: oldest element is silently dropped when full
Span<int> ring = stackalloc int[3];
var rb = new ValueRingBuffer<int>(ring);
rb.Write(10);
rb.Write(20);
rb.Write(30);
rb.Write(40); // 10 is overwritten; buffer now holds [20, 30, 40]
int first = rb.Read(); // 20 — FIFO order

// Indexer: random-access without removal (0 = oldest)
Span<int> ring2 = stackalloc int[4];
var rb2 = new ValueRingBuffer<int>(ring2);
rb2.Write(1);
rb2.Write(2);
rb2.Write(3);
int oldest = rb2[0]; // 1
int newest = rb2[rb2.Count - 1]; // 3

// ToArray: snapshot FIFO contents to a heap array
int[] snapshot = rb2.ToArray(); // [1, 2, 3]

// Drain: copy FIFO contents to a span and reset count to zero
Span<int> dest = stackalloc int[rb2.Count];
rb2.Drain(dest); // dest == [1, 2, 3]; rb2.Count == 0

_ = first;
_ = oldest;
_ = newest;
_ = snapshot;

Example - ValueByteBuffer

// Assemble a binary packet on the stack — no heap allocations until ToArray/CopyTo
Span<byte> buf = stackalloc byte[64];
using var bb = new ValueByteBuffer(buf);
bb.WriteUInt16LittleEndian(0x0102); // 2 bytes: message type
bb.WriteInt32LittleEndian(42); // 4 bytes: payload length
bb.WriteUInt64LittleEndian(ulong.MaxValue); // 8 bytes: sequence number
bb.Write((byte)0xFF); // 1 byte:  flags

int length = bb.Length; // 15 — zero heap allocations so far
byte[] copy = bb.ToArray(); // snapshot to heap only when needed

// WriteEncoded: append encoded text directly to the byte buffer with any Encoding
Span<byte> buf2 = stackalloc byte[32];
using var bb2 = new ValueByteBuffer(buf2);
bb2.Write((byte)0x01); // record type
bb2.WriteEncoded("Caf\u00E9".AsSpan(), Encoding.Latin1);
ReadOnlySpan<byte> encoded = bb2.WrittenSpan; // [01 43 61 66 E9]

// WriteUtf8: format UTF-8-first values directly into the byte buffer without a char intermediary
Span<byte> buf3 = stackalloc byte[32];
using var bb3 = new ValueByteBuffer(buf3);
bb3.Write((byte)0x02); // record type
bb3.WriteUtf8(255, "X4");
ReadOnlySpan<byte> utf8Formatted = bb3.WrittenSpan; // [02 30 30 46 46]

// WriteTo: push the written span to a stream without first calling ToArray()
using var stream = new MemoryStream();
bb3.WriteTo(stream);
byte[] streamed = stream.ToArray();

_ = length;
_ = copy;
_ = encoded;
_ = utf8Formatted;
_ = streamed;

Example - ValueTokenizer

// Extract flat tokens — no heap allocations during scanning
var tokenizer = new ValueTokenizer("{hello}{world}".AsSpan());
while (tokenizer.TryReadNext(out ReadOnlySpan<char> token))
    _ = token; // use as ReadOnlySpan<char>; call .ToString() only when string is needed

// Custom delimiters — parentheses as open/close
var csv = new ValueTokenizer("(Alice)(Bob)(Carol)".AsSpan(), '(', ')');
csv.TryReadNext(out ReadOnlySpan<char> name0); // "Alice"
csv.TryReadNext(out ReadOnlySpan<char> name1); // "Bob"
csv.TryReadNext(out ReadOnlySpan<char> name2); // "Carol"

// Nested delimiters — inner pairs counted as one token
var src = new ValueTokenizer("{group {detail} text}".AsSpan());
src.TryReadNested(out ReadOnlySpan<char> nested); // "group {detail} text"

// Position tracking and reset
var t = new ValueTokenizer("{a}{b}".AsSpan());
t.TryReadNext(out _); // consumes {a}
int pos = t.Position; // 3 (past the closing brace)
t.Reset(); // back to 0

_ = name0;
_ = name1;
_ = name2;
_ = nested;
_ = pos;

Public API Reference

See docs/PublicApi.md for the complete auto-generated public API reference.

Note: docs/PublicApi.md is auto-updated by the ReadMeTest_PublicApi test on every dotnet test run. Do not edit it manually.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Bia.ValueBuffers:

Package Downloads
Maple.Text

MapleStory rich-text parsing: tokenizes and strips MapleStory custom markup for use in UI text rendering. Cross-platform, trimmable and AOT/NativeAOT compatible.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.0 0 4/12/2026
0.8.0 0 4/12/2026
0.7.0 26 4/11/2026
0.6.0 31 4/11/2026
0.5.0 24 4/11/2026
0.4.0 31 4/11/2026
0.3.0 34 4/11/2026
0.2.0 32 4/11/2026
0.1.0 149 4/4/2026