// 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);
}
}