// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Buffers; using System.Runtime.CompilerServices; namespace CommunityToolkit.Mvvm.Messaging.Internals; /// /// A simple buffer writer implementation using pooled arrays. /// /// The type of items to store in the list. /// /// This type is a to avoid the object allocation and to /// enable the pattern-based support. We aren't worried with consumers not /// using this type correctly since it's private and only accessible within the parent type. /// internal ref struct ArrayPoolBufferWriter { /// /// The default buffer size to use to expand empty arrays. /// private const int DefaultInitialBufferSize = 128; /// /// The underlying array. /// private T[] array; /// /// The span mapping to . /// /// All writes are done through this to avoid covariance checks. private Span span; /// /// The starting offset within . /// private int index; /// /// Creates a new instance of the struct. /// /// A new instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ArrayPoolBufferWriter Create() { ArrayPoolBufferWriter instance; instance.span = instance.array = ArrayPool.Shared.Rent(DefaultInitialBufferSize); instance.index = 0; return instance; } /// /// Gets a with the current items. /// public ReadOnlySpan Span { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this.span.Slice(0, this.index); } /// /// Adds a new item to the current collection. /// /// The item to add. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(T item) { Span span = this.span; int index = this.index; if ((uint)index < (uint)span.Length) { span[index] = item; this.index = index + 1; } else { ResizeBufferAndAdd(item); } } /// /// Resets the underlying array and the stored items. /// public void Reset() { Array.Clear(this.array, 0, this.index); this.index = 0; } /// /// Resizes when there is no space left for new items, then adds one /// /// The item to add. [MethodImpl(MethodImplOptions.NoInlining)] private void ResizeBufferAndAdd(T item) { T[] rent = ArrayPool.Shared.Rent(this.index << 2); Array.Copy(this.array, 0, rent, 0, this.index); Array.Clear(this.array, 0, this.index); ArrayPool.Shared.Return(this.array); this.span = this.array = rent; this.span[this.index++] = item; } /// public void Dispose() { Array.Clear(this.array, 0, this.index); ArrayPool.Shared.Return(this.array); } }