ISequenceOwner<T> Interface
ISequenceOwner<T> is the sequence-shaped counterpart to ISegmentOwner<T>,
and follows the same idea as IMemoryOwner<T>: the interface carries the payload, and
IDisposable carries the lifetime.
It exists because the sequences returned by ArrayPoolMemoryStream.GetReadOnlySequence() and
ArrayPoolBufferWriter<T>.GetReadOnlySequence() are borrowed — they stay valid only while their
producer lives, so the payload dies when the stream or writer is disposed. An ISequenceOwner<T>
owns the buffers instead, which lets a payload be produced in one place, handed on, and released
by whoever finishes with it.
Namespace
CryptoHives.Foundation.Memory.Buffers
Inheritance
IDisposable → ISequenceOwner<T>
Syntax
public interface ISequenceOwner<T> : IDisposable
Type Parameters
T — The element type of the sequence.
Overview
| Concern | Member |
|---|---|
| Data access | Sequence property |
| Size | Length, IsEmpty |
| Lifetime management | Dispose() (from IDisposable) |
There is deliberately no indexer and no re-window method, unlike ISegmentOwner<T>. A
ReadOnlySequence<T> is read-only, and narrowing it is already a cheap value operation that leaves
ownership untouched:
ReadOnlySequence<byte> view = owner.Sequence.Slice(start, length);
Implementations
| Source | What it owns | Dispose behaviour |
|---|---|---|
SequenceLease<T> |
Nothing itself — it holds the producer | Disposes the producer, which frees the buffers |
SegmentSequence<T> |
One adopted ISegmentOwner<T> |
Disposes the inner owner |
EmptySequence<T> |
Nothing | No-op |
SequenceLease<T> is what the producers hand out, and it is the one to reach for by default.
Important
SequenceLease<T> is a struct. Using it through this interface boxes it —
a measured 48 bytes — which is exactly the allocation it exists to avoid. Take the lease by its own
type unless you genuinely need to treat several owner kinds uniformly.
Members
Properties
| Property | Type | Description |
|---|---|---|
Sequence |
ReadOnlySequence<T> |
The payload. Valid until the owner is disposed. |
Length |
long |
Total number of elements. |
IsEmpty |
bool |
Whether the owner holds nothing. |
Methods
void Dispose();
Releases the underlying memory according to the concrete implementation.
Ownership Is Exclusive
There is no reference counting. A single holder is responsible for disposal, and reading
Sequence afterwards is undefined — with pooled buffers the arrays behind it may already have been
handed to another tenant.
If several consumers need the same payload, either keep the owner alive until the last of them is finished, or copy.
Usage
Letting a payload leave its scope
The cheap way is a SequenceLease<T>, which carries the producer along as the
disposable and costs nothing:
static SequenceLease<byte> BuildPayload()
{
var writer = ObjectPools.RentBufferWriter<byte>();
Serialize(writer);
return writer.LeaseSequence();
}
using SequenceLease<byte> payload = BuildPayload();
Process(payload.Sequence);
Note the producer is deliberately not disposed inside BuildPayload — the lease owns it now, and
disposing it there would release the very buffers the payload is made of.
Reading through a Stream API
ReadOnlySequenceMemoryStream wraps a sequence as a readable Stream, reading the buffers in place
rather than copying them, and leaves ownership where it is:
using SequenceLease<byte> payload = stream.LeaseSequence();
using var reader = new ReadOnlySequenceMemoryStream(payload.Sequence);
await client.UploadAsync(reader);
Feeding the cryptography package
The hash algorithms accept a ReadOnlySequence<byte> directly, so a multi-segment payload is hashed
without ever being flattened:
using SequenceLease<byte> payload = writer.LeaseSequence();
byte[] digest = Blake3.HashData(payload.Sequence);
Composing with segment ownership
Any ISegmentOwner<T> becomes a sequence owner through
SegmentSequence<T>, so code written against ISequenceOwner<T> works with
every segment strategy without caring which is underneath:
ISegmentOwner<byte> segment = PooledSegment<byte>.Rent(1024);
using ISequenceOwner<byte> owner = SegmentSequence<byte>.Create(segment);
Thread Safety
Instances are not thread-safe. The intended pattern is single-owner, short-lived, scoped with
using.
See Also
- SequenceLease<T>
- SegmentSequence<T>
- EmptySequence<T>
- ISegmentOwner<T>
- ArrayPoolBufferWriter<T>
- ArrayPoolMemoryStream
- Memory Package Overview
© 2026 The Keepers of the CryptoHives