csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
5.5 KiB
184 lines
5.5 KiB
// Copyright (c) Six Labors.
|
|
// Licensed under the Apache License, Version 2.0.
|
|
// Ported from: https://github.com/SixLabors/Fonts/
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace Avalonia.Utilities
|
|
{
|
|
/// <summary>
|
|
/// A helper type for avoiding allocations while building arrays.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of item contained in the array.</typeparam>
|
|
internal struct ArrayBuilder<T>
|
|
where T : struct
|
|
{
|
|
private const int DefaultCapacity = 4;
|
|
private const int MaxCoreClrArrayLength = 0x7FeFFFFF;
|
|
|
|
// Starts out null, initialized on first Add.
|
|
private T[] _data;
|
|
private int _size;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the number of items in the array.
|
|
/// </summary>
|
|
public int Length
|
|
{
|
|
get => _size;
|
|
|
|
set
|
|
{
|
|
if (value == _size)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (value > 0)
|
|
{
|
|
EnsureCapacity(value);
|
|
|
|
_size = value;
|
|
}
|
|
else
|
|
{
|
|
_size = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a reference to specified element of the array.
|
|
/// </summary>
|
|
/// <param name="index">The index of the element to return.</param>
|
|
/// <returns>The <typeparamref name="T"/>.</returns>
|
|
/// <exception cref="IndexOutOfRangeException">
|
|
/// Thrown when index less than 0 or index greater than or equal to <see cref="Length"/>.
|
|
/// </exception>
|
|
public ref T this[int index]
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
get
|
|
{
|
|
#if DEBUG
|
|
if (index.CompareTo(0) < 0 || index.CompareTo(_size) > 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(index));
|
|
}
|
|
#endif
|
|
|
|
return ref _data![index];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Appends a given number of empty items to the array returning
|
|
/// the items as a slice.
|
|
/// </summary>
|
|
/// <param name="length">The number of items in the slice.</param>
|
|
/// <param name="clear">Whether to clear the new slice, Defaults to <see langword="true"/>.</param>
|
|
/// <returns>The <see cref="ArraySlice{T}"/>.</returns>
|
|
public ArraySlice<T> Add(int length, bool clear = true)
|
|
{
|
|
var position = _size;
|
|
|
|
// Expand the array.
|
|
Length += length;
|
|
|
|
var slice = AsSlice(position, Length - position);
|
|
|
|
if (clear)
|
|
{
|
|
slice.Span.Clear();
|
|
}
|
|
|
|
return slice;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Appends the slice to the array copying the data across.
|
|
/// </summary>
|
|
/// <param name="value">The array slice.</param>
|
|
/// <returns>The <see cref="ArraySlice{T}"/>.</returns>
|
|
public ArraySlice<T> Add(in ArraySlice<T> value)
|
|
{
|
|
var position = _size;
|
|
|
|
// Expand the array.
|
|
Length += value.Length;
|
|
|
|
var slice = AsSlice(position, Length - position);
|
|
|
|
value.Span.CopyTo(slice.Span);
|
|
|
|
return slice;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the array.
|
|
/// Allocated memory is left intact for future usage.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
// No need to actually clear since we're not allowing reference types.
|
|
_size = 0;
|
|
}
|
|
|
|
private void EnsureCapacity(int min)
|
|
{
|
|
var length = _data?.Length ?? 0;
|
|
|
|
if (length >= min)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Same expansion algorithm as List<T>.
|
|
var newCapacity = length == 0 ? DefaultCapacity : (uint)length * 2u;
|
|
|
|
if (newCapacity > MaxCoreClrArrayLength)
|
|
{
|
|
newCapacity = MaxCoreClrArrayLength;
|
|
}
|
|
|
|
if (newCapacity < min)
|
|
{
|
|
newCapacity = (uint)min;
|
|
}
|
|
|
|
var array = new T[newCapacity];
|
|
|
|
if (_size > 0)
|
|
{
|
|
Array.Copy(_data!, array, _size);
|
|
}
|
|
|
|
_data = array;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the current state of the array as a slice.
|
|
/// </summary>
|
|
/// <returns>The <see cref="ArraySlice{T}"/>.</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ArraySlice<T> AsSlice() => AsSlice(Length);
|
|
|
|
/// <summary>
|
|
/// Returns the current state of the array as a slice.
|
|
/// </summary>
|
|
/// <param name="length">The number of items in the slice.</param>
|
|
/// <returns>The <see cref="ArraySlice{T}"/>.</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ArraySlice<T> AsSlice(int length) => new ArraySlice<T>(_data!, 0, length);
|
|
|
|
/// <summary>
|
|
/// Returns the current state of the array as a slice.
|
|
/// </summary>
|
|
/// <param name="start">The index at which to begin the slice.</param>
|
|
/// <param name="length">The number of items in the slice.</param>
|
|
/// <returns>The <see cref="ArraySlice{T}"/>.</returns>
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ArraySlice<T> AsSlice(int start, int length) => new ArraySlice<T>(_data!, start, length);
|
|
}
|
|
}
|
|
|