BufferLease¶
Nalix.Codec.Memory.BufferLease is a core type in the Nalix ecosystem representing an owned lease of a pooled byte[] array. It is rented from the underlying IBufferPoolManager and supports advanced features like slice ownership, reference counting, and zero-allocation object pooling for the lease shells themselves.
Core Concepts¶
1. Shell Pooling (Zero-Allocation)¶
To avoid Garbage Collection (GC) pressure from millions of lease allocations, BufferLease itself is pooled using a highly optimized, lock-free free-list (ConcurrentStack with atomic counting). When you call BufferLease.Rent(), you get a recycled shell object wrapped around a rented byte array. This makes the lease shell essentially allocation-free.
2. Slice Ownership & Zero-Copy Handoff¶
Unlike a standard byte[], a BufferLease can represent a specific slice of an array (via _start and Length). This is critical for zero-copy handoffs where a payload is located after a protocol header in the same rented array. You can use TakeOwnership to create a lease over just that slice.
3. Reference Counting¶
BufferLease supports reference counting via Retain(). If multiple consumers need to hold the same buffer (e.g., broadcasting a message to multiple clients), they can Retain() the lease. The underlying byte array is only returned to the pool when the final owner calls Dispose().
API Overview¶
Renting & Creating¶
Rent(int capacity, bool zeroOnDispose = false): Auto-rents a buffer and returns an empty slice. You write toSpanFulland then callCommitLength().CopyFrom(ReadOnlySpan<byte> src, bool zeroOnDispose = false): Creates a lease by copying source data into a newly rented buffer.TakeOwnership(byte[] buffer, int start, int length, bool zeroOnDispose = false): Wraps a slice of an already rented array, taking ownership of it.FromRented(byte[] buffer, int length): Wraps an entire rented array.
Buffer Access¶
Span: A read/writeSpan<byte>over the valid (committed) payload.SpanFull: A read/writeSpan<byte>over the entire available capacity.Memory: AReadOnlyMemory<byte>over the valid payload.Capacity: Total capacity of the owned slice.Length: The committed length of the payload.
Lifecycle Management¶
CommitLength(int length): Sets the valid payload length (used after writing toSpanFull).Retain(): Increments the reference count.Dispose(): Decrements the reference count and returns the buffer to the pool when it reaches zero.ReleaseOwnership(out byte[]? buffer, out int start, out int length): Detaches the underlying array, transferring ownership to the caller. Returnstrueif the buffer was successfully detached;falseif the lease has already been detached or has multiple references. Disposing the lease afterward does nothing.
Example Usage¶
// Rent a lease of 1024 bytes
using BufferLease lease = BufferLease.Rent(1024);
// Write data into the full capacity
int written = SerializeMyData(lease.SpanFull);
// Commit how much was actually written
lease.CommitLength(written);
// The payload is now accessible via lease.Span or lease.Memory
ProcessData(lease.Memory);
// The 'using' block automatically calls Dispose() to return the buffer.