DotNext by Roman Sakno

<PackageReference Include="DotNext" Version="3.0.0" />

 SparseBufferWriter<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; } } }