ArrayPoolBufferWriter<T> Class
A high-performance implementation of IBufferWriter<T> that uses pooled memory segments from ArrayPool<T>.
Namespace
CryptoHives.Foundation.Memory.Buffers
Inheritance
Object ? ArrayPoolBufferWriter<T>
Implements
IBufferWriter<T>IDisposable
Syntax
public sealed class ArrayPoolBufferWriter<T> : IBufferWriter<T>, IDisposable
Type Parameters
T - The type of elements in the buffer
Overview
ArrayPoolBufferWriter<T> provides an efficient way to build sequences of data using pooled memory segments. It implements IBufferWriter<T>, making it compatible with serializers and other APIs that write to buffers. The writer grows by allocating progressively larger chunks from the array pool, avoiding continuous reallocations.
Benefits
- Pooled Memory: Uses
ArrayPool<T>.Sharedto minimize allocations - ArrayPool Backed: Efficient recycling of arrays for high-performance scenarios
- Buffer Clear Option: Optionally clears arrays before returning to pool for privacy
- Progressive Growth: Chunks grow exponentially up to a maximum size
- Zero-Copy Access:
GetReadOnlySequence()provides direct access without copying - IBufferWriter Support: Works with
System.Text.Json, Protocol Buffers, and other modern serializers - Disposable: Returns arrays to the pool on disposal
- Configurable: Customizable chunk sizes and clearing behavior
Constructors
| Constructor | Description |
|---|---|
ArrayPoolBufferWriter() |
Creates with default settings (256-byte initial chunks, 64KB max) |
ArrayPoolBufferWriter(int defaultChunksize, int maxChunkSize) |
Creates with custom chunk sizes |
ArrayPoolBufferWriter(bool clearArray, int defaultChunksize, int maxChunkSize) |
Creates with full customization including array clearing |
Properties
| Property | Type | Description |
|---|---|---|
DefaultChunkSize |
int (static) |
Default initial chunk size (256 bytes) |
MaxChunkSize |
int (static) |
Default maximum chunk size (64KB) |
Methods
IBufferWriter Implementation
public void Advance(int count)
Advances the writer by the specified number of elements that were written to the span/memory obtained from GetSpan/GetMemory.
public Memory<T> GetMemory(int sizeHint = 0)
Returns a Memory<T> to write to. The memory is at least sizeHint elements large.
public Span<T> GetSpan(int sizeHint = 0)
Returns a Span<T> to write to. The span is at least sizeHint elements large.
Sequence Access
public ReadOnlySequence<T> GetReadOnlySequence()
Returns a ReadOnlySequence<T> representing all written data. The sequence is valid until the next write operation or disposal.
Disposal
public void Dispose()
Returns all pooled arrays to ArrayPool<T>.Shared and invalidates the writer.
Usage Examples
Basic Usage
using var writer = new ArrayPoolBufferWriter<byte>();
// Get span and write
Span<byte> span = writer.GetSpan(100);
for (int i = 0; i < 100; i++)
{
span[i] = (byte)i;
}
writer.Advance(100);
// Get the result
ReadOnlySequence<byte> sequence = writer.GetReadOnlySequence();
With JSON Serialization
using var writer = new ArrayPoolBufferWriter<byte>();
using var jsonWriter = new Utf8JsonWriter(writer);
jsonWriter.WriteStartObject();
jsonWriter.WriteString("name"u8, "value"u8);
jsonWriter.WriteEndObject();
await jsonWriter.FlushAsync();
ReadOnlySequence<byte> jsonBytes = writer.GetReadOnlySequence();
mqttClient.Publish("topic", jsonBytes);
Building Protocol Messages
using var writer = new ArrayPoolBufferWriter<byte>();
// Write header
Span<byte> header = writer.GetSpan(4);
BinaryPrimitives.WriteInt32LittleEndian(header, messageId);
writer.Advance(4);
// Write payload
payload.CopyTo(writer.GetSpan(payload.Length));
writer.Advance(payload.Length);
ReadOnlySequence<byte> message = writer.GetReadOnlySequence();
Performance Characteristics
- Memory Allocation: array allocations as chunks overflow, but size grows exponentially to upper limit
- Write Operations: O(1) amortized for sequential writes
- Sequence Access: O(n) to get
ReadOnlySequence<T> - Disposal: O(n) (returns arrays to pool)
(where n is number of memory chunks)
Configuration
Chunk Growth Strategy
The writer starts with defaultChunksize and doubles the chunk size on each allocation until reaching maxChunkSize:
// Start with 1KB, grow to max 16KB
using var writer = new ArrayPoolBufferWriter<byte>(
defaultChunksize: 1024,
maxChunkSize: 16384
);
Array Clearing
For sensitive data, enable array clearing before returning to pool:
using var writer = new ArrayPoolBufferWriter<byte>(
clearArray: true,
defaultChunksize: 4096,
maxChunkSize: 65536
);
Thread Safety
⚠️ Not thread-safe. External synchronization required for concurrent access.
Best Practices
DO: Dispose Properly
using var writer = new ArrayPoolBufferWriter<byte>();
// Use writer...
// Automatically disposed and arrays returned
DO: Provide Size Hints
// If you know the size, provide a hint
Span<byte> span = writer.GetSpan(sizeHint: 1024);
DON'T: Use ReadOnlySequence After More Writes
var sequence1 = writer.GetReadOnlySequence();
writer.GetSpan(100); // This invalidates sequence1!
DO: Get Sequence Once at the End
// Write all data
WriteData(writer);
// Get sequence once at the end
ReadOnlySequence<byte> finalSequence = writer.GetReadOnlySequence();
Comparison with Alternatives
| Approach | Allocations | LOH Pressure | Complexity |
|---|---|---|---|
List<T> + ToArray() |
High | High for large data | Low |
MemoryStream |
Medium | Medium | Low |
ArrayPoolBufferWriter<T> |
Low | Low | Medium |
| Manual pooling | Lowest | Lowest | High |
See Also
© 2025 The Keepers of the CryptoHives