DotNext by .NET Foundation and Contributors

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

.NET API 494,488 bytes

 SparseBufferWriter<T>

Represents builder of the sparse memory buffer.
using DotNext.Runtime.CompilerServices; using System; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace DotNext.Buffers { [NullableContext(1)] [Nullable(0)] [DebuggerDisplay("WrittenCount = {WrittenCount}, FragmentedBytes = {FragmentedBytes}")] public class SparseBufferWriter<[Nullable(2)] T> : Disposable, IGrowableBuffer<T>, IReadOnlySpanConsumer<T>, ISupplier<ReadOnlyMemory<T>, CancellationToken, ValueTask>, IFunctional<Func<ReadOnlyMemory<T>, CancellationToken, ValueTask>>, IDisposable, IResettable, ISupplier<ReadOnlySequence<T>>, IFunctional<Func<ReadOnlySequence<T>>>, IEnumerable<ReadOnlyMemory<T>>, IEnumerable, IBufferWriter<T> { internal abstract class MemoryChunk : Disposable { internal abstract int FreeCapacity { get; } internal abstract Memory<T> FreeMemory { get; } internal abstract ReadOnlyMemory<T> WrittenMemory { get; } internal MemoryChunk Next { get; set; } internal SequencePosition StartOfNextChunk => new SequencePosition(Next, 0); internal SequencePosition EndOfChunk => new SequencePosition(this, WrittenMemory.Length); private protected MemoryChunk(MemoryChunk previous) { if (previous != null) previous.Next = this; } internal abstract int Write(ReadOnlySpan<T> input); protected override void Dispose(bool disposing) { if (disposing) Next = null; base.Dispose(disposing); } } private sealed class ImportedMemoryChunk : MemoryChunk { [CompilerGenerated] private ReadOnlyMemory<T> <memory>P = memory; internal override ReadOnlyMemory<T> WrittenMemory { get; } internal override int FreeCapacity => 0; internal override Memory<T> FreeMemory => Memory<T>.Empty; public ImportedMemoryChunk(ReadOnlyMemory<T> memory, MemoryChunk previous = null) : base(previous) { } internal override int Write(ReadOnlySpan<T> input) { return 0; } } private sealed class PooledMemoryChunk : MemoryChunk { private MemoryOwner<T> owner = Memory.AllocateAtLeast<T>(allocator, length); private int writtenCount; internal bool IsUnused => writtenCount == 0; internal override Memory<T> FreeMemory => owner.Memory.Slice(writtenCount); [DebuggerBrowsable(DebuggerBrowsableState.Never)] private Span<T> FreeSpan { get { return owner.Span.Slice(writtenCount); } } internal override int FreeCapacity => owner.Length - writtenCount; internal override ReadOnlyMemory<T> WrittenMemory => owner.Memory.Slice(0, writtenCount); public PooledMemoryChunk(MemoryAllocator<T> allocator, int length, MemoryChunk previous = null) : base(previous) { } internal override int Write(ReadOnlySpan<T> input) { Span.CopyTo<T>(input, FreeSpan, out int num); writtenCount += num; return num; } internal void Advance(int count) { int num = writtenCount + count; ArgumentOutOfRangeException.ThrowIfGreaterThan<uint>((uint)num, (uint)owner.Length, "length"); writtenCount = num; } internal void Realloc(MemoryAllocator<T> allocator, int length) { owner.Dispose(); owner = Memory.AllocateAtLeast<T>(allocator, length); } protected override void Dispose(bool disposing) { if (disposing) owner.Dispose(); writtenCount = 0; base.Dispose(disposing); } } [StructLayout(LayoutKind.Auto)] [NullableContext(0)] public struct Enumerator : IEnumerator<ReadOnlyMemory<T>>, IEnumerator, IDisposable { private MemoryChunk current; private bool initialized; [Nullable(new byte[] { 0, 1 })] public ReadOnlyMemory<T> Current { [IsReadOnly] [return: Nullable(new byte[] { 0, 1 })] get { if (current != null) return current.WrittenMemory; return ReadOnlyMemory<T>.Empty; } } [Nullable(1)] object IEnumerator.Current { [IsReadOnly] get { return Current; } } internal Enumerator(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); } } private readonly int chunkSize; private readonly MemoryAllocator<T> allocator; private readonly IntPtr growth; private int chunkIndex; private MemoryChunk first; private MemoryChunk last; private long length; [Nullable(new byte[] { 2, 0 })] internal MemoryChunk FirstChunk { get { return first; } } public long WrittenCount { get { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); return length; } } public bool IsSingleSegment => first == last; [DebuggerBrowsable(DebuggerBrowsableState.Never)] [ExcludeFromCodeCoverage] 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 SequencePosition End { get { MemoryChunk memoryChunk = last; if (memoryChunk == null) return default(SequencePosition); return memoryChunk.EndOfChunk; } } public SequencePosition Start => new SequencePosition(first, 0); public unsafe SparseBufferWriter(int chunkSize, SparseBufferGrowth growth = SparseBufferGrowth.None, [Nullable(new byte[] { 2, 1 })] MemoryAllocator<T> allocator = null) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero<int>(chunkSize, "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; } public unsafe SparseBufferWriter(MemoryPool<T> pool) { chunkSize = -1; allocator = Memory.ToAllocator<T>(pool); growth = (IntPtr)(void*); } public SparseBufferWriter() : this(MemoryPool<T>.Shared) { } public bool TryGetWrittenContent([Nullable(new byte[] { 0, 1 })] out ReadOnlyMemory<T> segment) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); if (IsSingleSegment) { segment = ((first == null) ? ReadOnlyMemory<T>.Empty : first.WrittenMemory); return true; } segment = default(ReadOnlyMemory<T>); return false; } public void Write([Nullable(new byte[] { 0, 1 })] ReadOnlySpan<T> input) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); 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([Nullable(new byte[] { 0, 1 })] ReadOnlyMemory<T> input, bool copyMemory = true) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); 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 Add(T item) { Write(MemoryMarshal.CreateReadOnlySpan<T>(ref item, 1)); } void IGrowableBuffer<T>.Write(T value) { Add(value); } public void Write([In] [IsReadOnly] [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); } } public void CopyTo<TConsumer>(TConsumer consumer) where TConsumer : IReadOnlySpanConsumer<T> { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); for (MemoryChunk next = this.first; next != null; next = next.Next) { ref TConsumer reference = ref consumer; TConsumer val = default(TConsumer); if (val == null) { val = reference; ref reference = ref val; } ((IReadOnlySpanConsumer<T>)reference).Invoke(next.WrittenMemory.Span); } } [AsyncStateMachine(typeof(<DotNext-Buffers-IGrowableBuffer<T>-CopyToAsync>d__25<>))] ValueTask IGrowableBuffer<T>.CopyToAsync<TConsumer>(TConsumer consumer, CancellationToken token) { <DotNext-Buffers-IGrowableBuffer<T>-CopyToAsync>d__25<TConsumer> stateMachine = default(<DotNext-Buffers-IGrowableBuffer<T>-CopyToAsync>d__25<TConsumer>); stateMachine.<>t__builder = AsyncValueTaskMethodBuilder.Create(); stateMachine.<>4__this = this; stateMachine.consumer = consumer; stateMachine.token = token; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start<<DotNext-Buffers-IGrowableBuffer<T>-CopyToAsync>d__25<TConsumer>>(ref stateMachine); return stateMachine.<>t__builder.Task; } public void CopyTo<[Nullable(2)] TArg>(ReadOnlySpanAction<T, TArg> writer, TArg arg) { CopyTo(new DelegatingReadOnlySpanConsumer<T, TArg>(writer, arg)); } public int CopyTo([Nullable(new byte[] { 0, 1 })] Span<T> output) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); 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() { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); ReleaseChunks(); length = 0; } ReadOnlySequence<T> ISupplier<ReadOnlySequence<T>>.Invoke() { if (!TryGetWrittenContent(out ReadOnlyMemory<T> segment)) return Memory.ToReadOnlySequence<T>((IEnumerable<ReadOnlyMemory<T>>)this); return new ReadOnlySequence<T>(segment); } 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); } public override string ToString() { if (!(typeof(T) == typeof(char)) || length > 2147483647) return GetType().ToString(); return <ToString>g__BuildString|32_1(first, (int)length); } public SequencePosition GetPosition(long offset, SequencePosition origin = default(SequencePosition)) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); if (offset == 0) return origin; NormalizePosition(ref origin); while (offset > 0) { MemoryChunk memoryChunk = origin.GetObject() as MemoryChunk; if (memoryChunk == null) break; long num = memoryChunk.WrittenMemory.Length - origin.GetInteger(); if (offset <= num) return new SequencePosition(memoryChunk, (int)offset); offset -= num; origin = new SequencePosition(memoryChunk.Next, 0); } throw new ArgumentOutOfRangeException("offset"); } private static ReadOnlyMemory<T> Read(ref SequencePosition position, ref long count) { MemoryChunk memoryChunk = position.GetObject() as MemoryChunk; if (memoryChunk == null || count == 0) return ReadOnlyMemory<T>.Empty; int integer = position.GetInteger(); ReadOnlyMemory<T> readOnlyMemory = memoryChunk.WrittenMemory.Slice(integer); if (readOnlyMemory.IsEmpty) { if (memoryChunk.Next == null) return ReadOnlyMemory<T>.Empty; position = memoryChunk.StartOfNextChunk; return Read(ref position, ref count); } if (readOnlyMemory.Length < count) { position = ((memoryChunk.Next == null) ? memoryChunk.EndOfChunk : memoryChunk.StartOfNextChunk); count -= readOnlyMemory.Length; return readOnlyMemory; } ReadOnlyMemory<T> result = readOnlyMemory; result = result.Slice(0, (int)count); count = 0; position = new SequencePosition(memoryChunk, integer + result.Length); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void NormalizePosition([ScopedRef] ref SequencePosition position) { if (!(position.GetObject() is MemoryChunk)) position = new SequencePosition(first, position.GetInteger()); } [return: Nullable(new byte[] { 0, 1 })] public ReadOnlySequence<T> Read(ref SequencePosition position, long count) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); ArgumentOutOfRangeException.ThrowIfNegative<long>(count, "count"); NormalizePosition(ref position); Chunk<T> chunk = null; Chunk<T> chunk2 = null; while (true) { ReadOnlyMemory<T> segment = Read(ref position, ref count); if (segment.IsEmpty) break; Chunk<T>.AddChunk(ref segment, ref chunk, ref chunk2); } if (count > 0) throw new InvalidOperationException(ExceptionMessages.EndOfBuffer(count)); if (chunk == null || chunk2 == null) return ReadOnlySequence<T>.Empty; if (chunk == chunk2) return new ReadOnlySequence<T>(chunk.Memory); return Chunk<T>.CreateSequence(chunk, chunk2); } public int CopyTo([ScopedRef] [Nullable(new byte[] { 0, 1 })] Span<T> output, [ScopedRef] ref SequencePosition position) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); NormalizePosition(ref position); int num = 0; long count = output.Length; while (true) { ReadOnlyMemory<T> readOnlyMemory = Read(ref position, ref count); if (readOnlyMemory.IsEmpty) break; readOnlyMemory.Span.CopyTo(output); num += readOnlyMemory.Length; output = output.Slice(readOnlyMemory.Length); } return num; } public void CopyTo<TConsumer>(TConsumer consumer, SequencePosition start) where TConsumer : IReadOnlySpanConsumer<T> { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); this.NormalizePosition(ref start); long count = this.length; while (true) { ReadOnlyMemory<T> readOnlyMemory = SparseBufferWriter<T>.Read(ref start, ref count); if (readOnlyMemory.IsEmpty) break; ref TConsumer reference = ref consumer; TConsumer val = default(TConsumer); if (val == null) { val = reference; ref reference = ref val; } ((IReadOnlySpanConsumer<T>)reference).Invoke(readOnlyMemory.Span); } } public long CopyTo<TConsumer>(TConsumer consumer, [ScopedRef] ref SequencePosition position, long count) where TConsumer : IReadOnlySpanConsumer<T> { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); ArgumentOutOfRangeException.ThrowIfNegative<long>(count, "count"); this.NormalizePosition(ref position); long num = 0; while (true) { ReadOnlyMemory<T> readOnlyMemory = SparseBufferWriter<T>.Read(ref position, ref count); if (readOnlyMemory.IsEmpty) break; ref TConsumer reference = ref consumer; TConsumer val = default(TConsumer); if (val == null) { val = reference; ref reference = ref val; } ((IReadOnlySpanConsumer<T>)reference).Invoke(readOnlyMemory.Span); num += readOnlyMemory.Length; } return num; } [NullableContext(0)] public Enumerator GetEnumerator() { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); return new Enumerator(first); } IEnumerator<ReadOnlyMemory<T>> IEnumerable<ReadOnlyMemory<T>>.GetEnumerator() { if (first != null) return GetEnumerator(); return Enumerable.Empty<ReadOnlyMemory<T>>().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } private Memory<T> GetMemory() { MemoryChunk memoryChunk = last; if (memoryChunk != null) { if (memoryChunk.FreeCapacity == 0) { MemoryAllocator<T> memoryAllocator = allocator; IntPtr intPtr = growth; last = new PooledMemoryChunk(memoryAllocator, (int), last); } } else first = (last = new PooledMemoryChunk(allocator, chunkSize, null)); return last.FreeMemory; } private Memory<T> GetMemory(int sizeHint) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); ArgumentOutOfRangeException.ThrowIfNegative<int>(sizeHint, "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; } Memory<T> IBufferWriter<T>.GetMemory(int sizeHint) { return GetMemory(sizeHint); } Span<T> IBufferWriter<T>.GetSpan(int sizeHint) { return GetMemory(sizeHint).Span; } void IBufferWriter<T>.Advance(int count) { ObjectDisposedException.ThrowIf(base.IsDisposed, (object)this); PooledMemoryChunk obj = last as PooledMemoryChunk; if (obj == null) throw new InvalidOperationException(); obj.Advance(count); length += count; } } }