SparseBufferWriter<T>
public class SparseBufferWriter<T> : Disposable, IEnumerable<ReadOnlyMemory<T>>, IEnumerable, IGrowableBuffer<T>, IReadOnlySpanConsumer<T>, ISupplier<ReadOnlyMemory<T>, CancellationToken, ValueTask>, IDisposable, ISupplier<ReadOnlySequence<T>>, IBufferWriter<T>
Represents builder of the sparse memory buffer.
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace DotNext.Buffers
{
[DebuggerDisplay("WrittenCount = {WrittenCount}, FragmentedBytes = {FragmentedBytes}")]
public class SparseBufferWriter<[System.Runtime.CompilerServices.Nullable(2)] T> : Disposable, IEnumerable<ReadOnlyMemory<T>>, IEnumerable, IGrowableBuffer<T>, IReadOnlySpanConsumer<T>, ISupplier<ReadOnlyMemory<T>, CancellationToken, ValueTask>, IDisposable, ISupplier<ReadOnlySequence<T>>, IBufferWriter<T>
{
[StructLayout(LayoutKind.Auto)]
public struct Enumerator : IEnumerator<ReadOnlyMemory<T>>, IEnumerator, IDisposable
{
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
private MemoryChunk current;
private bool initialized;
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
public ReadOnlyMemory<T> Current {
[IsReadOnly]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get {
if (current != null)
return current.WrittenMemory;
return ReadOnlyMemory<T>.Empty;
}
}
[System.Runtime.CompilerServices.Nullable(1)]
object IEnumerator.Current {
[IsReadOnly]
[System.Runtime.CompilerServices.NullableContext(1)]
get {
return Current;
}
}
internal Enumerator([System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})] MemoryChunk head)
{
current = head;
initialized = false;
}
public bool MoveNext()
{
if (initialized) {
MemoryChunk memoryChunk = current;
current = ((memoryChunk != null) ? memoryChunk.Next : null);
} else
initialized = true;
return current != null;
}
[IsReadOnly]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
void IDisposable.Dispose()
{
this = default(Enumerator);
}
}
internal abstract class MemoryChunk : Disposable
{
internal abstract int FreeCapacity { get; }
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
internal abstract Memory<T> FreeMemory {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get;
}
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
internal abstract ReadOnlyMemory<T> WrittenMemory {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get;
}
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
internal MemoryChunk Next {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
get;
[param: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
private set;
}
private protected MemoryChunk([System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})] MemoryChunk previous)
{
if (previous != null)
previous.Next = this;
}
internal abstract int Write([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<T> input);
protected override void Dispose(bool disposing)
{
if (disposing)
Next = null;
base.Dispose(disposing);
}
}
private sealed class ImportedMemoryChunk : MemoryChunk
{
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
[field: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
internal override ReadOnlyMemory<T> WrittenMemory {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get;
}
internal override int FreeCapacity => 0;
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
internal override Memory<T> FreeMemory {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get {
return Memory<T>.Empty;
}
}
internal ImportedMemoryChunk([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlyMemory<T> memory, [System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})] MemoryChunk previous = null)
: base(previous)
{
WrittenMemory = memory;
}
internal override int Write([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<T> input)
{
return 0;
}
}
private sealed class PooledMemoryChunk : MemoryChunk
{
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
private MemoryOwner<T> owner;
private int writtenCount;
internal bool IsUnused => writtenCount == 0;
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
internal override Memory<T> FreeMemory {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get {
return owner.Memory.Slice(writtenCount);
}
}
internal override int FreeCapacity => owner.Length - writtenCount;
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
internal override ReadOnlyMemory<T> WrittenMemory {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
get {
return owner.Memory.Slice(0, writtenCount);
}
}
internal PooledMemoryChunk([System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})] MemoryAllocator<T> allocator, int length, [System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})] MemoryChunk previous = null)
: base(previous)
{
owner = MemoryAllocator.Invoke<T>(allocator, length, false);
}
internal override int Write([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<T> input)
{
Span.CopyTo<T>(input, FreeMemory.Span, out int num);
writtenCount += num;
return num;
}
internal void Advance(int count)
{
int num = checked(writtenCount + count);
if (num > owner.Length)
throw new ArgumentOutOfRangeException("count");
writtenCount = num;
}
internal void Realloc([System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})] MemoryAllocator<T> allocator, int length)
{
owner.Dispose();
owner = MemoryAllocator.Invoke<T>(allocator, length, false);
}
protected override void Dispose(bool disposing)
{
owner.Dispose();
owner = default(MemoryOwner<T>);
writtenCount = 0;
base.Dispose(disposing);
}
}
private readonly int chunkSize;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})]
private readonly MemoryAllocator<T> allocator;
private readonly IntPtr growth;
private int chunkIndex;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
private MemoryChunk first;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
private MemoryChunk last;
private long length;
[System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
internal MemoryChunk FirstChunk {
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
2,
0
})]
get {
return first;
}
}
public long WrittenCount {
get {
ThrowIfDisposed();
return length;
}
}
private long FragmentedBytes {
get {
long num = 0;
MemoryChunk next;
for (MemoryChunk memoryChunk = first; memoryChunk != null; memoryChunk = next) {
next = memoryChunk.Next;
if (next != null && next.WrittenMemory.Length > 0)
num += memoryChunk.FreeCapacity;
}
return num;
}
}
public unsafe SparseBufferWriter(int chunkSize, SparseBufferGrowth growth = SparseBufferGrowth.None, [System.Runtime.CompilerServices.Nullable(new byte[] {
2,
1
})] MemoryAllocator<T> allocator = null)
{
if (chunkSize <= 0)
throw new ArgumentOutOfRangeException("chunkSize");
this.chunkSize = chunkSize;
this.allocator = allocator;
IntPtr intPtr;
switch (growth) {
case SparseBufferGrowth.Linear:
intPtr = (IntPtr)(void*);
break;
case SparseBufferGrowth.Exponential:
intPtr = (IntPtr)(void*);
break;
default:
intPtr = (IntPtr)(void*);
break;
}
this.growth = intPtr;
}
[System.Runtime.CompilerServices.NullableContext(1)]
public unsafe SparseBufferWriter(MemoryPool<T> pool)
{
chunkSize = -1;
allocator = MemoryAllocator.ToAllocator<T>(pool);
growth = (IntPtr)(void*);
}
public SparseBufferWriter()
: this(MemoryPool<T>.Shared)
{
}
public void Write([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<T> input)
{
ThrowIfDisposed();
if (last == null)
first = (last = new PooledMemoryChunk(allocator, chunkSize, null));
while (!input.IsEmpty) {
int num = last.Write(input);
if (num == 0) {
MemoryAllocator<T> memoryAllocator = allocator;
IntPtr intPtr = growth;
last = new PooledMemoryChunk(memoryAllocator, (int), last);
} else
input = input.Slice(num);
length += num;
}
}
public void Write([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlyMemory<T> input, bool copyMemory = true)
{
ThrowIfDisposed();
if (!input.IsEmpty) {
if (copyMemory)
Write(input.Span);
else if (last == null) {
first = (last = new ImportedMemoryChunk(input, null));
length += input.Length;
} else {
last = new ImportedMemoryChunk(input, last);
length += input.Length;
}
}
}
public void Write([In] [IsReadOnly] [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ref ReadOnlySequence<T> sequence, bool copyMemory = true)
{
ReadOnlySequence<T>.Enumerator enumerator = sequence.GetEnumerator();
while (enumerator.MoveNext()) {
ReadOnlyMemory<T> current = enumerator.Current;
Write(current, copyMemory);
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void CopyTo<TConsumer>(TConsumer consumer) where TConsumer : IReadOnlySpanConsumer<T>
{
ThrowIfDisposed();
for (MemoryChunk next = this.first; next != null; next = next.Next) {
ReadOnlySpan<T> span = next.WrittenMemory.Span;
((IReadOnlySpanConsumer<T>)consumer).Invoke(span);
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void CopyTo<[System.Runtime.CompilerServices.Nullable(2)] TArg>(ReadOnlySpanAction<T, TArg> writer, TArg arg)
{
CopyTo(new DelegatingReadOnlySpanConsumer<T, TArg>(writer, arg));
}
public int CopyTo([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] Span<T> output)
{
ThrowIfDisposed();
int num = 0;
MemoryChunk next = first;
while (next != null && !output.IsEmpty) {
Span.CopyTo<T>(next.WrittenMemory.Span, output, out int writtenCount);
output = output.Slice(writtenCount);
num += writtenCount;
next = next.Next;
}
return num;
}
public void Clear()
{
ThrowIfDisposed();
ReleaseChunks();
length = 0;
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
ReadOnlySequence<T> ISupplier<ReadOnlySequence<T>>.Invoke()
{
if (first == null)
return ReadOnlySequence<T>.Empty;
if (first.Next == null)
return new ReadOnlySequence<T>(first.WrittenMemory);
return BufferHelpers.ToReadOnlySequence<T>((IEnumerable<ReadOnlyMemory<T>>)this);
}
public Enumerator GetEnumerator()
{
ThrowIfDisposed();
return new Enumerator(first);
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0,
1
})]
IEnumerator<ReadOnlyMemory<T>> IEnumerable<ReadOnlyMemory<T>>.GetEnumerator()
{
return GetEnumerator();
}
[System.Runtime.CompilerServices.NullableContext(1)]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void ReleaseChunks()
{
MemoryChunk next;
for (MemoryChunk memoryChunk = first; memoryChunk != null; memoryChunk = next) {
next = memoryChunk.Next;
memoryChunk.Dispose();
}
first = (last = null);
}
protected override void Dispose(bool disposing)
{
if (disposing)
ReleaseChunks();
base.Dispose(disposing);
}
[System.Runtime.CompilerServices.NullableContext(1)]
public override string ToString()
{
if (!(typeof(T) == typeof(char)) || length > 2147483647)
return GetType().ToString();
return <ToString>g__BuildString|29_1(first, (int)length);
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
private Memory<T> GetMemory()
{
if (last == null)
first = (last = new PooledMemoryChunk(allocator, chunkSize, null));
else if (last.FreeCapacity == 0) {
MemoryAllocator<T> memoryAllocator = allocator;
IntPtr intPtr = growth;
last = new PooledMemoryChunk(memoryAllocator, (int), last);
}
return last.FreeMemory;
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
private Memory<T> GetMemory(int sizeHint)
{
ThrowIfDisposed();
if (sizeHint < 0)
throw new ArgumentOutOfRangeException("sizeHint");
if (sizeHint == 0)
return GetMemory();
if (last == null)
first = (last = new PooledMemoryChunk(allocator, sizeHint, null));
else if (last.FreeCapacity == 0) {
last = new PooledMemoryChunk(allocator, sizeHint, last);
} else if (last.FreeCapacity < sizeHint) {
PooledMemoryChunk pooledMemoryChunk = last as PooledMemoryChunk;
if (pooledMemoryChunk != null && pooledMemoryChunk.IsUnused)
pooledMemoryChunk.Realloc(allocator, sizeHint);
else
last = new PooledMemoryChunk(allocator, sizeHint, last);
}
return last.FreeMemory;
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
Memory<T> IBufferWriter<T>.GetMemory(int sizeHint)
{
return GetMemory(sizeHint);
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})]
Span<T> IBufferWriter<T>.GetSpan(int sizeHint)
{
return GetMemory(sizeHint).Span;
}
void IBufferWriter<T>.Advance(int count)
{
ThrowIfDisposed();
PooledMemoryChunk obj = last as PooledMemoryChunk;
if (obj == null)
throw new InvalidOperationException();
obj.Advance(count);
length += count;
}
}
}