/// <summary> /// Initializes growable buffer. /// </summary> /// <param name="buffer">Pre-allocated buffer used by this builder.</param> /// <param name="allocator">The memory allocator used to rent the memory blocks.</param> /// <remarks> /// The builder created with this constructor is growable. However, additional memory will not be /// requested using <paramref name="allocator"/> while <paramref name="buffer"/> space is sufficient. /// If <paramref name="allocator"/> is <see langword="null"/> then <see cref="ArrayPool{T}.Shared"/> /// is used for memory pooling. /// </remarks> public BufferWriterSlim(Span <T> buffer, MemoryAllocator <T>?allocator = null) { initialBuffer = buffer; this.allocator = allocator; extraBuffer = default; position = 0; }
protected override void Dispose(bool disposing) { owner.Dispose(); owner = default; writtenCount = 0; base.Dispose(disposing); }
/// <summary> /// Clears the data written to the underlying buffer. /// </summary> /// <exception cref="ObjectDisposedException">This writer has been disposed.</exception> public override void Clear() { ThrowIfDisposed(); buffer.Dispose(); buffer = default; position = 0; }
public static void RentFromMemoryPool2() { using var owner = new MemoryOwner <byte>(MemoryPool <byte> .Shared.Rent, 10); Equal(10, owner.Memory.Length); owner[1] = 42; Equal(42, owner[1]); }
/// <inheritdoc/> private protected override void Resize(int newSize) { var newBuffer = allocator.Invoke(newSize, false); buffer.Memory.CopyTo(newBuffer.Memory); buffer.Dispose(); buffer = newBuffer; }
/// <summary> /// Initializes growable builder. /// </summary> /// <param name="buffer">Pre-allocated buffer used by this builder.</param> /// <param name="copyOnOverflow"> /// <see langword="true"/> to copy pre-allocated buffer to pooled memory on overflow; /// <see langword="false"/> to keep head of the written content in the pre-allocated buffer. /// </param> /// <param name="allocator">The memory allocator used to rent the memory blocks.</param> /// <remarks> /// The builder created with this constructor is growable. However, additional memory will not be /// requested using <paramref name="allocator"/> while <paramref name="buffer"/> space is sufficient. /// If <paramref name="allocator"/> is <see langword="null"/> then <see cref="ArrayPool{T}.Shared"/> /// is used for memory pooling. /// <see cref="WrittenSpan"/> property is supported only if <paramref name="copyOnOverflow"/> is <see langword="true"/>. /// Otherwise, it's not possible to represent written content as contiguous memory block. /// </remarks> public BufferWriterSlim(Span <T> buffer, bool copyOnOverflow, MemoryAllocator <T>?allocator = null) { initialBuffer = buffer; this.allocator = allocator; extraBuffer = default; position = 0; this.copyOnOverflow = copyOnOverflow; }
public static void WrapArray() { var array = new byte[42]; using var owner = new MemoryOwner <byte>(array); Equal(42, owner.Memory.Length); owner[2] = 10; Equal(10, array[2]); }
/// <inheritdoc/> private protected override void Resize(int newSize) { var newBuffer = allocator.Invoke(newSize, false); buffer.Memory.CopyTo(newBuffer.Memory); buffer.Dispose(); buffer = newBuffer; AllocationCounter?.WriteMetric(newBuffer.Length); }
/// <summary> /// Initializes a new writer with the specified initial capacity. /// </summary> /// <param name="allocator">The allocator of internal buffer.</param> /// <param name="initialCapacity">The initial capacity of the writer.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="initialCapacity"/> is less than or equal to zero.</exception> public PooledBufferWriter(MemoryAllocator <T>?allocator, int initialCapacity) { if (initialCapacity <= 0) { throw new ArgumentOutOfRangeException(nameof(initialCapacity)); } this.allocator = allocator; buffer = allocator.Invoke(initialCapacity, false); }
/// <summary> /// Allocates memory. /// </summary> /// <param name="allocator">The memory allocator.</param> /// <param name="length">The number of items in the rented memory.</param> /// <param name="exactSize"> /// <see langword="true"/> to ask allocator to allocate exactly <paramref name="length"/>; /// <see langword="false"/> to allocate at least <paramref name="length"/>. /// </param> /// <typeparam name="T">The type of the items in the memory pool.</typeparam> /// <returns>The allocated memory.</returns> public static MemoryOwner <T> Invoke <T>(this MemoryAllocator <T> allocator, int length, bool exactSize) { var result = allocator(length); if (!exactSize) { MemoryOwner <T> .Expand(ref result); } return(result); }
/// <inheritdoc/> protected override void Dispose(bool disposing) { if (disposing) { buffer.Dispose(); buffer = default; } base.Dispose(disposing); }
/// <inheritdoc/> protected override void Dispose(bool disposing) { if (disposing) { BufferSizeCallback?.Invoke(buffer.Length); buffer.Dispose(); buffer = default; } base.Dispose(disposing); }
public override void Clear() => Clear(false); // TODO: Remove this method in future /// <summary> /// Clears the data written to the underlying memory. /// </summary> /// <param name="reuseBuffer"><see langword="true"/> to reuse the internal buffer; <see langword="false"/> to destroy the internal buffer.</param> /// <exception cref="ObjectDisposedException">This writer has been disposed.</exception> public override void Clear(bool reuseBuffer) { ThrowIfDisposed(); if (!reuseBuffer) { buffer.Dispose(); buffer = default; } position = 0; }
public static void RawReference() { var owner = new MemoryOwner <byte>(Array.Empty <byte>()); True(Unsafe.IsNullRef(ref BufferHelpers.GetReference(in owner))); owner = default; True(Unsafe.IsNullRef(ref BufferHelpers.GetReference(in owner))); owner = new(new byte[] { 10 }); Equal(10, BufferHelpers.GetReference(in owner)); }
/// <summary> /// Initializes growable buffer. /// </summary> /// <param name="initialCapacity">The initial capacity of the internal buffer.</param> /// <param name="allocator">The memory allocator used to rent the memory blocks.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="initialCapacity"/> is less than zero.</exception> public BufferWriterSlim(int initialCapacity, MemoryAllocator <T>?allocator = null) { if (initialCapacity < 0) { throw new ArgumentOutOfRangeException(nameof(initialCapacity)); } initialBuffer = default; this.allocator = allocator; extraBuffer = initialCapacity == 0 ? default : allocator.Invoke(initialCapacity, false); position = 0; }
public static void SetBufferLength() { var buffer = default(MemoryOwner <byte>); True(buffer.TryResize(0)); False(buffer.TryResize(10)); Throws <ArgumentOutOfRangeException>(() => buffer.TryResize(-1)); buffer = new MemoryOwner <byte>(new byte[] { 10, 20, 30 }); True(buffer.TryResize(1)); True(buffer.TryResize(3)); False(buffer.TryResize(10)); }
/// <summary> /// Detaches the underlying buffer with written content from this writer. /// </summary> /// <param name="owner">The buffer owner.</param> /// <returns> /// <see langword="true"/> if the written content is in rented buffer because initial buffer overflows; /// <see langword="false"/> if the written content is in preallocated buffer. /// </returns> public bool TryDetachBuffer(out MemoryOwner <T> owner) { if (NoOverflow) { owner = default; return(false); } owner = extraBuffer; owner.Truncate(position); position = 0; extraBuffer = default; return(true); }
/// <summary> /// lears the data written to the underlying buffer. /// </summary> /// <param name="reuseBuffer"><see langword="true"/> to reuse the internal buffer; <see langword="false"/> to destroy the internal buffer.</param> public void Clear(bool reuseBuffer) { initialBuffer.Clear(); if (!reuseBuffer) { extraBuffer.Dispose(); extraBuffer = default; } else if (RuntimeHelpers.IsReferenceOrContainsReferences <T>()) { extraBuffer.Memory.Span.Clear(); } position = 0; }
public override void Clear() => Clear(false); // TODO: Remove this method in future /// <summary> /// Clears the data written to the underlying memory. /// </summary> /// <param name="reuseBuffer"><see langword="true"/> to reuse the internal buffer; <see langword="false"/> to destroy the internal buffer.</param> /// <exception cref="ObjectDisposedException">This writer has been disposed.</exception> public override void Clear(bool reuseBuffer) { ThrowIfDisposed(); if (!reuseBuffer) { buffer.Dispose(); buffer = default; } else if (RuntimeHelpers.IsReferenceOrContainsReferences <T>()) { buffer.Memory.Span.Clear(); } position = 0; }
/// <summary> /// Returns the memory to write to that is at least the requested size. /// </summary> /// <param name="sizeHint">The minimum length of the returned memory.</param> /// <returns>The memory block of at least the size <paramref name="sizeHint"/>.</returns> /// <exception cref="OutOfMemoryException">The requested buffer size is not available.</exception> public Span <T> GetSpan(int sizeHint = 0) { if (sizeHint < 0) { throw new ArgumentOutOfRangeException(nameof(sizeHint)); } Span <T> result; int? newSize; if (extraBuffer.IsEmpty) { newSize = IGrowableBuffer <T> .GetBufferSize(sizeHint, initialBuffer.Length, position); // need to copy initial buffer if (newSize.HasValue) { extraBuffer = allocator.Invoke(newSize.GetValueOrDefault(), false); initialBuffer.CopyTo(extraBuffer.Memory.Span); initialBuffer.Clear(); result = extraBuffer.Memory.Span; } else { result = initialBuffer; } } else { newSize = IGrowableBuffer <T> .GetBufferSize(sizeHint, extraBuffer.Length, position); // no need to copy initial buffer if (newSize.HasValue) { var newBuffer = allocator.Invoke(newSize.GetValueOrDefault(), false); extraBuffer.Memory.CopyTo(newBuffer.Memory); extraBuffer.Dispose(); extraBuffer = newBuffer; } result = extraBuffer.Memory.Span; } return(result.Slice(position)); }
public Memory <byte> GetMemory(int sizeHint) { if (sizeHint < 0) { throw new ArgumentOutOfRangeException(nameof(sizeHint)); } if (sizeHint == 0) { sizeHint = 1; } if (sizeHint > buffer.Length - position) { buffer.Dispose(); buffer = allocator(sizeHint); } return(buffer.Memory.Slice(position)); }
public static MemoryOwner <T> Invoke <T>(this MemoryAllocator <T>?allocator, int length, bool exactSize) { MemoryOwner <T> result; if (allocator is null) { result = new MemoryOwner <T>(ArrayPool <T> .Shared, length, exactSize); goto exit; } result = allocator(length); if (!exactSize) { result.Expand(); } exit: return(result); }
/// <inheritdoc /> public override MemoryOwner <T> DetachBuffer() { ThrowIfDisposed(); MemoryOwner <T> result; if (position > 0) { result = buffer; buffer = default; result.Truncate(position); position = 0; } else { result = default; } return(result); }
public void Advance(int count) { if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (position > buffer.Length - count) { throw new InvalidOperationException(); } output.Write(buffer.Memory.Span.Slice(position, count)); position += count; // release buffer if it is cannot be reused if (position == buffer.Length) { buffer.Dispose(); buffer = default; position = 0; } }
/// <summary> /// Writes elements to this buffer. /// </summary> /// <param name="input">The span of elements to be written.</param> /// <exception cref="InsufficientMemoryException">Pre-allocated initial buffer size is not enough to place <paramref name="input"/> elements to it and this builder is not growable.</exception> /// <exception cref="OverflowException">The size of the internal buffer becomes greater than <see cref="int.MaxValue"/>.</exception> public void Write(ReadOnlySpan <T> input) { var newSize = checked (position + input.Length); Span <T> output; int offset; MemoryOwner <T> newBuffer; if (copyOnOverflow) { // grow if needed if (newSize > initialBuffer.Length) { if (extraBuffer.IsEmpty) { extraBuffer = allocator.Invoke(newSize, false); initialBuffer.CopyTo(extraBuffer.Memory.Span); initialBuffer.Clear(); } else if (newSize > extraBuffer.Length) { newBuffer = allocator.Invoke(newSize, false); extraBuffer.Memory.CopyTo(newBuffer.Memory); extraBuffer.Dispose(); extraBuffer = newBuffer; } output = extraBuffer.Memory.Span; } else { output = initialBuffer; } offset = position; } else { // grow if needed if (newSize > initialBuffer.Length && newSize - initialBuffer.Length > extraBuffer.Length) { newBuffer = allocator.Invoke(newSize, false); extraBuffer.Memory.CopyTo(newBuffer.Memory); extraBuffer.Dispose(); extraBuffer = newBuffer; } // append elements if (position < initialBuffer.Length) { var writtenCount = Math.Min(initialBuffer.Length - position, input.Length); input.Slice(0, writtenCount).CopyTo(initialBuffer.Slice(position)); input = input.Slice(writtenCount); offset = 0; } else { offset = position - initialBuffer.Length; } output = extraBuffer.Memory.Span; } input.CopyTo(output.Slice(offset)); position = newSize; }
internal PooledMemoryChunk(MemoryAllocator <T>?allocator, int length, MemoryChunk?previous = null) : base(previous) { owner = allocator.Invoke(length, false); }
internal void Realloc(MemoryAllocator <T>?allocator, int length) { owner.Dispose(); owner = allocator.Invoke(length, false); }
public static void DefaultValue() { using var owner = new MemoryOwner <decimal>(); True(owner.IsEmpty); True(owner.Memory.IsEmpty); }
public static void RentFromMemoryPoolDefaultSize() { using var owner = new MemoryOwner <byte>(MemoryPool <byte> .Shared); False(owner.Memory.IsEmpty); }
internal static void Expand(ref MemoryOwner <T> owner) => Unsafe.AsRef(in owner.length) = owner.RealLength;