Faactory.Channels.Buffers 2.8.2

dotnet add package Faactory.Channels.Buffers --version 2.8.2
                    
NuGet\Install-Package Faactory.Channels.Buffers -Version 2.8.2
                    
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="Faactory.Channels.Buffers" Version="2.8.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Faactory.Channels.Buffers" Version="2.8.2" />
                    
Directory.Packages.props
<PackageReference Include="Faactory.Channels.Buffers" />
                    
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 Faactory.Channels.Buffers --version 2.8.2
                    
#r "nuget: Faactory.Channels.Buffers, 2.8.2"
                    
#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 Faactory.Channels.Buffers@2.8.2
                    
#: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=Faactory.Channels.Buffers&version=2.8.2
                    
Install as a Cake Addin
#tool nuget:?package=Faactory.Channels.Buffers&version=2.8.2
                    
Install as a Cake Tool

Channels - Buffers

A lightweight library for reading and writing byte buffers.

Although originally designed for Channels and part of the same project, this package has no dependency on Channels and can be used independently.

Design

Buffer instances are always specialized for either reading or writing, never both.

  • Use IReadableByteBuffer for reading
  • Use IWritableByteBuffer for writing

This separation allows each implementation to be optimized for its specific purpose.

Using Buffers

When using Channels, you usually don’t need to manually create buffer instances. The pipeline can automatically convert between byte[] and IReadableByteBuffer.

Outside of Channels, or when needed explicitly:

  • Use WritableByteBuffer to create a writable buffer.
  • Use ReadableByteBuffer to wrap a byte[].

Reading Data

There are four ways to retrieve data:

  • Get performs a passive read at a specified offset (does not change buffer offset)
  • Read performs an active read at the current offset and advances it
  • TryRead performs tentative reads that only advance the offset if successful
  • Checkpoints allow speculative multi-step reads with rollback support
IReadableByteBuffer buffer = ...;

// Reads at a custom offset (does not change buffer offset)
var b1 = buffer.GetByte(customOffset);

// Reads at current offset and advances it by 1
var b2 = buffer.ReadByte();

// Attempts to read without throwing if insufficient data exists
if ( buffer.TryReadInt32( out var value ) )
{
    // value successfully read
}

// Speculative read with rollback support
using var checkpoint = buffer.Checkpoint();

try
{
    var b3 = buffer.ReadByte();
    var i1 = buffer.ReadInt32();

    // Commit the checkpoint to make the offset changes permanent
    checkpoint.Commit();
}
catch ( ArgumentOutOfRangeException )
{
    // Rollback to the checkpointed offset if not committed
}

Renting Buffers from a Pool

For scenarios that create many temporary writable buffers (for example when encoding messages), the library provides ByteBufferPool.

The pool rents buffers backed by reusable byte[] instances, reducing allocations and GC pressure.

Example:

var pool = new ByteBufferPool();

using var buffer = pool.Rent();

buffer.WriteInt32( 123 );
buffer.WriteByte( 1 );

var readable = buffer.AsReadableView();

Buffers rented from the pool must be disposed when no longer needed so the underlying byte array can be returned to the pool.

You can also request a specific minimum capacity:

using var buffer = pool.Rent( 4096 );

The returned buffer behaves exactly like a regular WritableByteBuffer. If additional capacity is required while writing, the buffer will automatically grow as needed, just like a regular writable buffer. When this happens, the previously rented array is returned to the pool and a larger one is rented.

When disposed:

  • the underlying byte array is returned to the pool
  • the buffer instance becomes unusable

Tracked Buffer Pool

For scenarios where many buffers are rented within a defined scope, the library also provides TrackedByteBufferPool.

This pool tracks all rented buffers and automatically disposes any that were not manually released when the pool itself is disposed.

Example:

using var pool = new TrackedByteBufferPool();

var buffer1 = pool.Rent();
var buffer2 = pool.Rent();

buffer1.WriteInt32( 123 );

// disposing the pool automatically disposes any remaining buffers

This can be useful when buffers are created throughout a workflow and manual disposal could be easily forgotten.

Rented buffers can be disposed manually. The tracked pool still keeps the reference until the scope ends, but disposal is idempotent.

Buffer Views (Zero-Copy)

Buffers in v2.x support windowed views, allowing efficient access to subsets of data without copying.

Windowed Readable Buffers

ReadableByteBuffer can represent:

  • The entire underlying byte[] or
  • A windowed view over a portion of a byte[]

Windowed views are also created internally when using:

  • GetByteBuffer( offset, length )
  • ReadByteBuffer( length )

These methods do not allocate.
Instead, they create a readable view over the same underlying array.

This significantly reduces allocations in high-throughput scenarios.

A windowed view shares the underlying array. If you need to persist the data beyond the lifetime of the original buffer, call ToArray() to create a copy.

Readable Views over Writable Buffers

IWritableByteBuffer.AsReadableView() creates a readable view over the currently written portion of the writable buffer.

  • No allocation occurs.
  • The returned buffer shares the underlying array.
  • The readable view is valid as long as the writable buffer is not modified or compacted.

Example:

var writable = new WritableByteBuffer();

writable.WriteInt32( 123 );

var readable = writable.AsReadableView(); // zero-copy

The readable view reflects the writable buffer’s current contents. If the writable buffer is modified, compacted, or resized, the view may no longer represent the same logical data.

If a stable snapshot is required, create a copy:

var snapshot = readable.ToArray();

Writable Views

WritableByteBuffer.CreateView( offset ) creates a windowed writable view starting at the specified offset. They allow writing to a specific portion of the buffer without affecting the main write cursor.

The returned view:

  • Shares the same underlying memory as the parent buffer (zero-copy)
  • Allows overwriting data within the already written portion
  • Cannot grow the parent buffer
  • Cannot write beyond the already written portion of the parent buffer at the time of view creation
  • Maintains its own write cursor independent from the parent

Example:

var buffer = new WritableByteBuffer();

buffer.WriteBytes( [1, 2, 3, 4, 5] );

// create a writable view starting at offset 2
// writing to the view modifies the parent buffer at the corresponding positions
var view = buffer.CreateView( 2 )
    .WriteBytes( [9, 9] );

// buffer now contains: [1, 2, 9, 9, 5]

Writable views are limited to the portion of the buffer that existed when the view was created.
If the parent buffer grows after the view is created, the view cannot access or write into the new region.
Views should not be used after the parent buffer has been modified or compacted, as their behavior becomes undefined.

Writable views do not support operations that would alter the parent buffer’s structure (such as Clear, Compact or Rebase).

ToArray() Behavior

ToArray() on a readable buffer returns:

  • The backing array if the readable buffer owns it entirely
  • A copy if the readable buffer is a windowed view

This ensures:

  • No unnecessary allocations for full buffers
  • Safe behavior for windowed or writable-backed views

On writable buffers, ToArray() always creates a copy of the currently written portion, as writable buffers do not guarantee stable underlying data.

Summary of Allocation Behavior

Operation Allocates?
GetByteBuffer ❌ No
ReadByteBuffer ❌ No
AsReadableView ❌ No
ToArray() (full-owned buffer) ❌ No
ToArray() (windowed view) ✅ Yes

These improvements significantly reduce allocations in decoding pipelines and high-throughput scenarios while maintaining safety when snapshots are required.

Good to know...

  • ReadableBytes exposes how many bytes remain unread.
  • ResetOffset resets the read offset (undoes reads).
  • ToArray returns the entire buffer, regardless of offset.
  • Except for reading/getting methods, the API follows a fluent design.
  • IWritableByteBuffer is not expected in middleware.

Output Pipeline Notes

While middleware in the input pipeline is expected to use IReadableByteBuffer, the output pipeline is more permissive.

In the output pipeline:

  • byte[]
  • IReadableByteBuffer
  • IByteBuffer
  • IWritableByteBuffer

are all supported and ultimately treated as byte[] before being written to the transport.

This allows adapters and handlers in the output pipeline to work with writable buffers when building outgoing payloads, while preserving the strict readable-only design in the input pipeline.

Migrating from v1.x

In v1.x, a single IByteBuffer interface handled both reading and writing. Consumers had to:

  • Check readability/writability
  • Or handle runtime exceptions

In v2.x:

  • IByteBuffer now contains only common members.
  • IReadableByteBuffer and IWritableByteBuffer inherit from it.
  • Read and write responsibilities are clearly separated.

Migration Guidelines

  • Replace IByteBuffer with:
  • IReadableByteBuffer for reading scenarios
  • IWritableByteBuffer for writing scenarios
  • Most APIs remain the same, so migration is typically straightforward.
  • Middleware should use IReadableByteBuffer.
  • IWritableByteBuffer is not expected in middleware.
  • Automatic conversion from byte[] to IWritableByteBuffer is not supported.
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Faactory.Channels.Buffers:

Package Downloads
Faactory.Channels

Channels

Faactory.Channels.Abstractions

Channels

Faactory.Channels.Core

Channels

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.8.2 76 6/4/2026
2.8.1 192 5/29/2026
2.8.0 177 5/29/2026
2.7.0 164 5/28/2026
2.6.0 190 5/27/2026
2.5.2 219 5/25/2026
2.5.1 187 5/21/2026
2.5.0 159 5/20/2026
2.4.0 191 5/19/2026
2.3.0 233 3/6/2026
2.2.1 157 3/6/2026
2.2.0 159 3/5/2026
2.1.0 228 2/25/2026
2.0.0 186 2/23/2026
2.0.0-preview.10 63 2/23/2026
2.0.0-preview.9 62 2/23/2026
2.0.0-preview.8 62 2/23/2026
2.0.0-preview.7 66 2/23/2026
2.0.0-preview.6 68 2/20/2026
2.0.0-preview.5 70 2/20/2026
Loading failed