aspnetcore/src/Microsoft.AspNetCore.Blazor/RenderTree/ArrayBuilder.cs

120 lines
3.7 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Runtime.CompilerServices;
namespace Microsoft.AspNetCore.Blazor.RenderTree
{
/// <summary>
/// Implements a list that uses an array of objects to store the elements.
/// </summary>
internal class ArrayBuilder<T>
{
private const int MinCapacity = 10;
private T[] _items;
private int _itemsInUse;
/// <summary>
/// Constructs a new instance of <see cref="ArrayBuilder{T}"/>.
/// </summary>
public ArrayBuilder() : this(MinCapacity)
{
}
/// <summary>
/// Constructs a new instance of <see cref="ArrayBuilder{T}"/>.
/// </summary>
public ArrayBuilder(int capacity)
{
_items = new T[capacity < MinCapacity ? MinCapacity : capacity];
_itemsInUse = 0;
}
/// <summary>
/// Gets the number of items.
/// </summary>
public int Count => _itemsInUse;
/// <summary>
/// Gets the underlying buffer.
/// </summary>
public T[] Buffer => _items;
/// <summary>
/// Appends a new item, automatically resizing the underlying array if necessary.
/// </summary>
/// <param name="item">The item to append.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Just like System.Collections.Generic.List<T>
public void Append(in T item)
{
if (_itemsInUse == _items.Length)
{
SetCapacity(_itemsInUse * 2, preserveContents: true);
}
_items[_itemsInUse++] = item;
}
/// <summary>
/// Removes the last item.
/// </summary>
public void RemoveLast()
{
_itemsInUse--;
_items[_itemsInUse] = default(T); // Release to GC
}
/// <summary>
/// Marks the array as empty, also shrinking the underlying storage if it was
/// not being used to near its full capacity.
/// </summary>
public void Clear()
{
var previousItemsInUse = _itemsInUse;
_itemsInUse = 0;
if (_items.Length > previousItemsInUse * 1.5)
{
SetCapacity((previousItemsInUse + _items.Length) / 2, preserveContents: false);
}
else
{
Array.Clear(_items, 0, _itemsInUse); // Release to GC
}
}
/// <summary>
/// Produces an <see cref="ArrayRange{T}"/> structure describing the current contents.
/// </summary>
/// <returns>The <see cref="ArrayRange{T}"/>.</returns>
public ArrayRange<T> ToRange()
=> new ArrayRange<T>(_items, _itemsInUse);
private void SetCapacity(int desiredCapacity, bool preserveContents)
{
if (desiredCapacity < _itemsInUse)
{
throw new ArgumentOutOfRangeException(nameof(desiredCapacity), $"The value cannot be less than {nameof(Count)}");
}
var newCapacity = desiredCapacity < MinCapacity ? MinCapacity : desiredCapacity;
if (newCapacity != _items.Length)
{
var newItems = new T[newCapacity];
if (preserveContents)
{
Array.Copy(_items, newItems, _itemsInUse);
}
_items = newItems;
}
else if (!preserveContents)
{
Array.Clear(_items, 0, _items.Length);
}
}
}
}