Salar.BinaryBuffers
4.0.1
dotnet add package Salar.BinaryBuffers --version 4.0.1
NuGet\Install-Package Salar.BinaryBuffers -Version 4.0.1
<PackageReference Include="Salar.BinaryBuffers" Version="4.0.1" />
<PackageVersion Include="Salar.BinaryBuffers" Version="4.0.1" />
<PackageReference Include="Salar.BinaryBuffers" />
paket add Salar.BinaryBuffers --version 4.0.1
#r "nuget: Salar.BinaryBuffers, 4.0.1"
#:package Salar.BinaryBuffers@4.0.1
#addin nuget:?package=Salar.BinaryBuffers&version=4.0.1
#tool nuget:?package=Salar.BinaryBuffers&version=4.0.1
BinaryBuffers
BinaryBuffers is a high-performance .NET library for reading and writing primitive values directly from binary buffers. It gives you BinaryReader/BinaryWriter-style APIs without requiring an intermediate Stream, which reduces allocations and improves throughput in buffer-heavy workloads.
Why BinaryBuffers?
- Use the recommended span-based reader and writer for direct
byte[]access - Work directly with
byte[],Span<byte>, andReadOnlySpan<byte>buffers - Reuse existing buffers with
ResetBuffer(...) - Read from
ReadOnlyMemory<byte>andReadOnlySequence<byte> - Use shared abstractions through
IBufferReaderandIBufferWriter - Swap in stream-based compatibility types when you still need a
Stream
Installation
dotnet add package Salar.BinaryBuffers
Quick start
When working directly with a byte[], Span<byte>, or ReadOnlySpan<byte>, prefer BinarySpanBufferWriter and BinarySpanBufferReader. They provide the recommended zero-allocation, lowest-overhead path.
Recommended span-based API
using Salar.BinaryBuffers;
var buffer = new byte[32];
var writer = new BinarySpanBufferWriter(buffer);
writer.Write(2022);
writer.Write(8.11);
var reader = new BinarySpanBufferReader(writer.ToReadOnlySpan());
var year = reader.ReadInt32();
var value = reader.ReadDouble();
BinarySpanBufferWriter and BinarySpanBufferReader are ref struct types, so they cannot be stored as class fields or used across async boundaries. In those cases, use BinaryBufferWriter and BinaryBufferReader; they are still very fast and can also be referenced through their shared interfaces.
Class-based API
using Salar.BinaryBuffers;
var buffer = new byte[32];
var writer = new BinaryBufferWriter(buffer);
writer.Write(2022);
writer.Write(8.11);
var bytesWritten = writer.WrittenLength;
var reader = new BinaryBufferReader(buffer, 0, bytesWritten);
var year = reader.ReadInt32();
var value = reader.ReadDouble();
Span-based reader and writer
BinarySpanBufferWriter and BinarySpanBufferReader are zero-allocation, high-performance types that operate directly on spans. As ref struct types, they can work with stack-allocated memory (stackalloc) without copying or allocating intermediate buffers.
BinarySpanBufferWriter
Use BinarySpanBufferWriter to write directly to a Span<byte>:
// Stack-allocated buffer — no heap allocation
Span<byte> buffer = stackalloc byte[1024];
var writer = new BinarySpanBufferWriter(buffer);
writer.Write(2022);
writer.Write(8.11);
// Get the written bytes as a ReadOnlySpan<byte>
ReadOnlySpan<byte> written = writer.ToReadOnlySpan();
BinarySpanBufferWriter implements IBufferWriter and works seamlessly with generic methods:
void Serialize<TBufferWriter>(TBufferWriter writer, int id) where TBufferWriter : IBufferWriter
{
writer.Write(id);
}
Span<byte> buffer = stackalloc byte[1024];
var writer = new BinarySpanBufferWriter(buffer);
Serialize(writer, 42); // Works via generic constraint — no boxing
BinarySpanBufferReader
Use BinarySpanBufferReader to read directly from a ReadOnlySpan<byte>:
ReadOnlySpan<byte> buffer = GetBuffer();
var reader = new BinarySpanBufferReader(buffer);
var id = reader.ReadInt32();
var amount = reader.ReadDouble();
Because they are ref struct types, span-based readers and writers cannot be stored as class fields, used across async boundaries, or boxed to interfaces directly. Use the still-high-performance BinaryBufferWriter and BinaryBufferReader when those capabilities are needed.
Additional Goodies
Use StreamBufferWriter as a drop-in replacement for BinaryWriter when a Stream is required.
BinaryBufferWriter
Reuse BinaryBufferReader, BinaryBufferWriter, BinarySpanBufferReader, and BinarySpanBufferWriter with ResetBuffer(...) instead of creating new instances.
using Salar.BinaryBuffers;
var buffer = new byte[128];
var writer = new BinaryBufferWriter(buffer);
writer.Write(42);
writer.Write(123.45m);
writer.ResetBuffer();
writer.Write(7);
BinaryBufferReader
Use BinaryBufferReader to read primitive values from a byte[] or ArraySegment<byte>.
using Salar.BinaryBuffers;
var payload = new byte[16];
var writer = new BinaryBufferWriter(payload);
writer.Write(42);
writer.Write(2.5f);
var reader = new BinaryBufferReader(payload);
var id = reader.ReadInt32();
var amount = reader.ReadSingle();
Additional readers and compatibility types
BinaryBufferMemoryReaderreads fromReadOnlyMemory<byte>SequenceBufferReaderreads fromReadOnlySequence<byte>StreamBufferWriteris a stream-based writer that implements the same writer abstractionStreamBufferReaderis a stream-based reader that integrates with the same reader abstraction
This makes it easier to program against IBufferReader and IBufferWriter instead of tying your code to a single storage model.
When to use it
BinaryBuffers is a good fit when you:
- already own the underlying byte buffer and can use the recommended span-based APIs
- want to avoid wrapping buffers in
MemoryStream - need predictable, low-allocation binary serialization of primitive values
- want to reuse the same buffer across repeated operations
Compatibility
BinaryBuffers does not support cross-endian binary data exchange.
Benchmarks
Benchmarks in this repository show substantial improvements for common primitive reads and writes when compared to BinaryReader and BinaryWriter.
Read benchmarks
Lower is better.
Int
| Method | Mean | Error | StdDev | Relative time |
|---|---|---|---|---|
BinaryReader_ReadInt (.NET built-in) |
25.64 ms | 0.2979 ms | 0.2787 ms | baseline |
BufferReader_ReadInt |
3.97 ms | 0.0675 ms | 0.1071 ms | -80% |
SpanBufferReader_ReadInt |
5.21 ms | 0.0596 ms | 0.0557 ms | -74% |
StreamBufferReader_ReadInt |
17.54 ms | 0.2505 ms | 0.2343 ms | -13% |
BinaryBufferMemoryReader_ReadInt |
12.35 ms | 0.1158 ms | 0.1026 ms | -39% |
SequenceBufferReader_ReadInt |
39.11 ms | 0.5776 ms | 0.5403 ms | +93% |
Decimal
| Method | Mean | Error | StdDev | Relative time |
|---|---|---|---|---|
BinaryReader_ReadDecimal (.NET built-in) |
20.26 ms | 0.2713 ms | 0.2405 ms | baseline |
BufferReader_ReadDecimal |
20.51 ms | 0.4102 ms | 0.8470 ms | +1% |
SpanBufferReader_ReadDecimal |
3.05 ms | 0.0378 ms | 0.0296 ms | -85% |
StreamBufferReader_ReadDecimal |
24.27 ms | 0.4706 ms | 0.5780 ms | +20% |
BinaryBufferMemoryReader_ReadDecimal |
23.76 ms | 0.1748 ms | 0.1635 ms | +17% |
SequenceBufferReader_ReadDecimal |
38.12 ms | 0.1591 ms | 0.1329 ms | +88% |
Float
| Method | Mean | Error | StdDev | Relative time |
|---|---|---|---|---|
BinaryReader_ReadFloat (.NET built-in) |
14.60 ms | 0.1148 ms | 0.1018 ms | baseline |
BufferReader_ReadFloat |
2.76 ms | 0.0544 ms | 0.0482 ms | -86% |
SpanBufferReader_ReadFloat |
3.36 ms | 0.0238 ms | 0.0199 ms | -83% |
StreamBufferReader_ReadFloat |
10.16 ms | 0.0977 ms | 0.0816 ms | -50% |
BinaryBufferMemoryReader_ReadFloat |
6.45 ms | 0.0704 ms | 0.0624 ms | -68% |
SequenceBufferReader_ReadFloat |
19.12 ms | 0.3742 ms | 0.3500 ms | -6% |
Write benchmarks
Lower is better.
Int
| Method | Mean | Error | StdDev | Relative time |
|---|---|---|---|---|
BinaryWriter_WriteInt (.NET built-in) |
62.53 ms | 1.2336 ms | 2.1279 ms | baseline |
BufferWriter_WriteInt |
11.60 ms | 0.2227 ms | 0.2383 ms | -43% |
SpanBufferWriter_WriteInt |
6.74 ms | 0.1292 ms | 0.1680 ms | -67% |
StreamWriter_WriteInt |
59.54 ms | 1.0247 ms | 0.9585 ms | +194% |
Decimal
| Method | Mean | Error | StdDev | Relative time |
|---|---|---|---|---|
BinaryWriter_WriteDecimal (.NET built-in) |
40.29 ms | 0.8057 ms | 0.8274 ms | baseline |
BufferWriter_WriteDecimal |
7.11 ms | 0.1013 ms | 0.0947 ms | -65% |
SpanBufferWriter_WriteDecimal |
5.49 ms | 0.0622 ms | 0.0611 ms | -73% |
StreamWriter_WriteDecimal |
33.87 ms | 0.3489 ms | 0.3093 ms | +67% |
Float
| Method | Mean | Error | StdDev | Relative time |
|---|---|---|---|---|
BinaryWriter_WriteFloat (.NET built-in) |
30.67 ms | 0.3490 ms | 0.3094 ms | baseline |
BufferWriter_WriteFloat |
6.07 ms | 0.1009 ms | 0.1080 ms | -70% |
SpanBufferWriter_WriteFloat |
3.41 ms | 0.0310 ms | 0.0290 ms | -83% |
StreamWriter_WriteFloat |
29.58 ms | 0.5377 ms | 0.5029 ms | +46% |
These benchmark results were recorded with the benchmark project in this repository using .NET 10.0 on:
AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Memory (>= 4.5.5)
-
net10.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Salar.BinaryBuffers:
| Package | Downloads |
|---|---|
|
Salar.Bois
The most compact, extremely fast binary serializer for .NET. More info: https://github.com/salarcode/Bois |
|
|
Salar.Bois.CodeGen
Salar's Serializer for Binary Object Indexed Serialization, the source-code-generator version. |
GitHub repositories
This package is not used by any popular GitHub repositories.
* Added BinarySpanBufferReader to work with Span<byte>
* Performance improvements
* Updated README with additional samples