9 changed files with 3036 additions and 4 deletions
@ -0,0 +1,37 @@ |
|||||
|
namespace Avalonia.Collections.Pooled |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This enum allows control over how data is treated when internal
|
||||
|
/// arrays are returned to the ArrayPool. Be careful to understand
|
||||
|
/// what each option does before using anything other than the default
|
||||
|
/// of Auto.
|
||||
|
/// </summary>
|
||||
|
public enum ClearMode |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// <para><code>Auto</code> has different behavior depending on the host project's target framework.</para>
|
||||
|
/// <para>.NET Core 2.1: Reference types and value types that contain reference types are cleared
|
||||
|
/// when the internal arrays are returned to the pool. Value types that do not contain reference
|
||||
|
/// types are not cleared when returned to the pool.</para>
|
||||
|
/// <para>.NET Standard 2.0: All user types are cleared before returning to the pool, in case they
|
||||
|
/// contain reference types.
|
||||
|
/// For .NET Standard, Auto and Always have the same behavior.</para>
|
||||
|
/// </summary>
|
||||
|
Auto = 0, |
||||
|
/// <summary>
|
||||
|
/// The <para><code>Always</code> setting has the effect of always clearing user types before returning to the pool.
|
||||
|
/// This is the default behavior on .NET Standard.</para><para>You might want to turn this on in a .NET Core project
|
||||
|
/// if you were concerned about sensitive data stored in value types leaking to other pars of your application.</para>
|
||||
|
/// </summary>
|
||||
|
Always = 1, |
||||
|
/// <summary>
|
||||
|
/// <para><code>Never</code> will cause pooled collections to never clear user types before returning them to the pool.</para>
|
||||
|
/// <para>You might want to use this setting in a .NET Standard project when you know that a particular collection stores
|
||||
|
/// only value types and you want the performance benefit of not taking time to reset array items to their default value.</para>
|
||||
|
/// <para>Be careful with this setting: if used for a collection that contains reference types, or value types that contain
|
||||
|
/// reference types, this setting could cause memory issues by making the garbage collector unable to clean up instances
|
||||
|
/// that are still being referenced by arrays sitting in the ArrayPool.</para>
|
||||
|
/// </summary>
|
||||
|
Never = 2 |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
// 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.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
|
||||
|
namespace Avalonia.Collections.Pooled |
||||
|
{ |
||||
|
internal sealed class ICollectionDebugView<T> |
||||
|
{ |
||||
|
private readonly ICollection<T> _collection; |
||||
|
|
||||
|
public ICollectionDebugView(ICollection<T> collection) |
||||
|
{ |
||||
|
_collection = collection ?? throw new ArgumentNullException(nameof(collection)); |
||||
|
} |
||||
|
|
||||
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] |
||||
|
public T[] Items |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
T[] items = new T[_collection.Count]; |
||||
|
_collection.CopyTo(items, 0); |
||||
|
return items; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Avalonia.Collections.Pooled |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a read-only collection of pooled elements that can be accessed by index
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The type of elements in the read-only pooled list.</typeparam>
|
||||
|
|
||||
|
public interface IReadOnlyPooledList<T> : IReadOnlyList<T> |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets a <see cref="System.ReadOnlySpan{T}"/> for the items currently in the collection.
|
||||
|
/// </summary>
|
||||
|
ReadOnlySpan<T> Span { get; } |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,699 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
/*============================================================================= |
||||
|
** |
||||
|
** |
||||
|
** Purpose: An array implementation of a generic stack. |
||||
|
** |
||||
|
** |
||||
|
=============================================================================*/ |
||||
|
|
||||
|
using System; |
||||
|
using System.Buffers; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Serialization; |
||||
|
using System.Threading; |
||||
|
|
||||
|
namespace Avalonia.Collections.Pooled |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A simple stack of objects. Internally it is implemented as an array,
|
||||
|
/// so Push can be O(n). Pop is O(1).
|
||||
|
/// </summary>
|
||||
|
[DebuggerTypeProxy(typeof(StackDebugView<>))] |
||||
|
[DebuggerDisplay("Count = {Count}")] |
||||
|
[Serializable] |
||||
|
public class PooledStack<T> : IEnumerable<T>, ICollection, IReadOnlyCollection<T>, IDisposable, IDeserializationCallback |
||||
|
{ |
||||
|
[NonSerialized] |
||||
|
private ArrayPool<T> _pool; |
||||
|
[NonSerialized] |
||||
|
private object _syncRoot; |
||||
|
|
||||
|
private T[] _array; // Storage for stack elements. Do not rename (binary serialization)
|
||||
|
private int _size; // Number of items in the stack. Do not rename (binary serialization)
|
||||
|
private int _version; // Used to keep enumerator in sync w/ collection. Do not rename (binary serialization)
|
||||
|
private readonly bool _clearOnFree; |
||||
|
|
||||
|
private const int DefaultCapacity = 4; |
||||
|
|
||||
|
#region Constructors
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with the default initial capacity.
|
||||
|
/// </summary>
|
||||
|
public PooledStack() : this(ClearMode.Auto, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with the default initial capacity.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(ClearMode clearMode) : this(clearMode, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with the default initial capacity.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(ArrayPool<T> customPool) : this(ClearMode.Auto, customPool) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with the default initial capacity and a custom ArrayPool.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(ClearMode clearMode, ArrayPool<T> customPool) |
||||
|
{ |
||||
|
_pool = customPool ?? ArrayPool<T>.Shared; |
||||
|
_array = Array.Empty<T>(); |
||||
|
_clearOnFree = ShouldClear(clearMode); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with a specific initial capacity. The initial capacity
|
||||
|
/// must be a non-negative number.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(int capacity) : this(capacity, ClearMode.Auto, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with a specific initial capacity. The initial capacity
|
||||
|
/// must be a non-negative number.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(int capacity, ClearMode clearMode) : this(capacity, clearMode, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with a specific initial capacity. The initial capacity
|
||||
|
/// must be a non-negative number.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(int capacity, ArrayPool<T> customPool) : this(capacity, ClearMode.Auto, customPool) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a stack with a specific initial capacity. The initial capacity
|
||||
|
/// must be a non-negative number.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(int capacity, ClearMode clearMode, ArrayPool<T> customPool) |
||||
|
{ |
||||
|
if (capacity < 0) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, |
||||
|
ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); |
||||
|
} |
||||
|
_pool = customPool ?? ArrayPool<T>.Shared; |
||||
|
_array = _pool.Rent(capacity); |
||||
|
_clearOnFree = ShouldClear(clearMode); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(IEnumerable<T> enumerable) : this(enumerable, ClearMode.Auto, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(IEnumerable<T> enumerable, ClearMode clearMode) : this(enumerable, clearMode, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(IEnumerable<T> enumerable, ArrayPool<T> customPool) : this(enumerable, ClearMode.Auto, customPool) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(IEnumerable<T> enumerable, ClearMode clearMode, ArrayPool<T> customPool) |
||||
|
{ |
||||
|
_pool = customPool ?? ArrayPool<T>.Shared; |
||||
|
_clearOnFree = ShouldClear(clearMode); |
||||
|
|
||||
|
switch (enumerable) |
||||
|
{ |
||||
|
case null: |
||||
|
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.enumerable); |
||||
|
break; |
||||
|
|
||||
|
case ICollection<T> collection: |
||||
|
if (collection.Count == 0) |
||||
|
{ |
||||
|
_array = Array.Empty<T>(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_array = _pool.Rent(collection.Count); |
||||
|
collection.CopyTo(_array, 0); |
||||
|
_size = collection.Count; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
using (var list = new PooledList<T>(enumerable)) |
||||
|
{ |
||||
|
_array = _pool.Rent(list.Count); |
||||
|
list.Span.CopyTo(_array); |
||||
|
_size = list.Count; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(T[] array) : this(array.AsSpan(), ClearMode.Auto, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(T[] array, ClearMode clearMode) : this(array.AsSpan(), clearMode, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(T[] array, ArrayPool<T> customPool) : this(array.AsSpan(), ClearMode.Auto, customPool) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(T[] array, ClearMode clearMode, ArrayPool<T> customPool) : this(array.AsSpan(), clearMode, customPool) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(ReadOnlySpan<T> span) : this(span, ClearMode.Auto, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(ReadOnlySpan<T> span, ClearMode clearMode) : this(span, clearMode, ArrayPool<T>.Shared) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(ReadOnlySpan<T> span, ArrayPool<T> customPool) : this(span, ClearMode.Auto, customPool) { } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills a Stack with the contents of a particular collection. The items are
|
||||
|
/// pushed onto the stack in the same order they are read by the enumerator.
|
||||
|
/// </summary>
|
||||
|
public PooledStack(ReadOnlySpan<T> span, ClearMode clearMode, ArrayPool<T> customPool) |
||||
|
{ |
||||
|
_pool = customPool ?? ArrayPool<T>.Shared; |
||||
|
_clearOnFree = ShouldClear(clearMode); |
||||
|
_array = _pool.Rent(span.Length); |
||||
|
span.CopyTo(_array); |
||||
|
_size = span.Length; |
||||
|
} |
||||
|
|
||||
|
#endregion
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The number of items in the stack.
|
||||
|
/// </summary>
|
||||
|
public int Count => _size; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the ClearMode behavior for the collection, denoting whether values are
|
||||
|
/// cleared from internal arrays before returning them to the pool.
|
||||
|
/// </summary>
|
||||
|
public ClearMode ClearMode => _clearOnFree ? ClearMode.Always : ClearMode.Never; |
||||
|
|
||||
|
bool ICollection.IsSynchronized => false; |
||||
|
|
||||
|
object ICollection.SyncRoot |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_syncRoot == null) |
||||
|
{ |
||||
|
Interlocked.CompareExchange<object>(ref _syncRoot, new object(), null); |
||||
|
} |
||||
|
return _syncRoot; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes all Objects from the Stack.
|
||||
|
/// </summary>
|
||||
|
public void Clear() |
||||
|
{ |
||||
|
if (_clearOnFree) |
||||
|
{ |
||||
|
Array.Clear(_array, 0, _size); // clear the elements so that the gc can reclaim the references.
|
||||
|
} |
||||
|
_size = 0; |
||||
|
_version++; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compares items using the default equality comparer
|
||||
|
/// </summary>
|
||||
|
public bool Contains(T item) |
||||
|
{ |
||||
|
// PERF: Internally Array.LastIndexOf calls
|
||||
|
// EqualityComparer<T>.Default.LastIndexOf, which
|
||||
|
// is specialized for different types. This
|
||||
|
// boosts performance since instead of making a
|
||||
|
// virtual method call each iteration of the loop,
|
||||
|
// via EqualityComparer<T>.Default.Equals, we
|
||||
|
// only make one virtual call to EqualityComparer.LastIndexOf.
|
||||
|
|
||||
|
return _size != 0 && Array.LastIndexOf(_array, item, _size - 1) != -1; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This method removes all items which match the predicate.
|
||||
|
/// The complexity is O(n).
|
||||
|
/// </summary>
|
||||
|
public int RemoveWhere(Func<T, bool> match) |
||||
|
{ |
||||
|
if (match == null) |
||||
|
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); |
||||
|
|
||||
|
int freeIndex = 0; // the first free slot in items array
|
||||
|
|
||||
|
// Find the first item which needs to be removed.
|
||||
|
while (freeIndex < _size && !match(_array[freeIndex])) |
||||
|
freeIndex++; |
||||
|
if (freeIndex >= _size) |
||||
|
return 0; |
||||
|
|
||||
|
int current = freeIndex + 1; |
||||
|
while (current < _size) |
||||
|
{ |
||||
|
// Find the first item which needs to be kept.
|
||||
|
while (current < _size && match(_array[current])) |
||||
|
current++; |
||||
|
|
||||
|
if (current < _size) |
||||
|
{ |
||||
|
// copy item to the free slot.
|
||||
|
_array[freeIndex++] = _array[current++]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (_clearOnFree) |
||||
|
{ |
||||
|
// Clear the removed elements so that the gc can reclaim the references.
|
||||
|
Array.Clear(_array, freeIndex, _size - freeIndex); |
||||
|
} |
||||
|
|
||||
|
int result = _size - freeIndex; |
||||
|
_size = freeIndex; |
||||
|
_version++; |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
// Copies the stack into an array.
|
||||
|
public void CopyTo(T[] array, int arrayIndex) |
||||
|
{ |
||||
|
if (array == null) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); |
||||
|
} |
||||
|
|
||||
|
if (arrayIndex < 0 || arrayIndex > array.Length) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex); |
||||
|
} |
||||
|
|
||||
|
if (array.Length - arrayIndex < _size) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall); |
||||
|
} |
||||
|
|
||||
|
Debug.Assert(array != _array); |
||||
|
int srcIndex = 0; |
||||
|
int dstIndex = arrayIndex + _size; |
||||
|
while (srcIndex < _size) |
||||
|
{ |
||||
|
array[--dstIndex] = _array[srcIndex++]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void CopyTo(Span<T> span) |
||||
|
{ |
||||
|
if (span.Length < _size) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentException_DestinationTooShort(); |
||||
|
} |
||||
|
|
||||
|
int srcIndex = 0; |
||||
|
int dstIndex = _size; |
||||
|
while (srcIndex < _size) |
||||
|
{ |
||||
|
span[--dstIndex] = _array[srcIndex++]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void ICollection.CopyTo(Array array, int arrayIndex) |
||||
|
{ |
||||
|
if (array == null) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); |
||||
|
} |
||||
|
|
||||
|
if (array.Rank != 1) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); |
||||
|
} |
||||
|
|
||||
|
if (array.GetLowerBound(0) != 0) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound, ExceptionArgument.array); |
||||
|
} |
||||
|
|
||||
|
if (arrayIndex < 0 || arrayIndex > array.Length) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex); |
||||
|
} |
||||
|
|
||||
|
if (array.Length - arrayIndex < _size) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
Array.Copy(_array, 0, array, arrayIndex, _size); |
||||
|
Array.Reverse(array, arrayIndex, _size); |
||||
|
} |
||||
|
catch (ArrayTypeMismatchException) |
||||
|
{ |
||||
|
ThrowHelper.ThrowArgumentException_Argument_InvalidArrayType(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns an IEnumerator for this PooledStack.
|
||||
|
/// </summary>
|
||||
|
/// <returns></returns>
|
||||
|
public Enumerator GetEnumerator() |
||||
|
=> new Enumerator(this); |
||||
|
|
||||
|
/// <internalonly/>
|
||||
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() |
||||
|
=> new Enumerator(this); |
||||
|
|
||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||
|
=> new Enumerator(this); |
||||
|
|
||||
|
public void TrimExcess() |
||||
|
{ |
||||
|
if (_size == 0) |
||||
|
{ |
||||
|
ReturnArray(replaceWith: Array.Empty<T>()); |
||||
|
_version++; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int threshold = (int)(_array.Length * 0.9); |
||||
|
if (_size < threshold) |
||||
|
{ |
||||
|
var newArray = _pool.Rent(_size); |
||||
|
if (newArray.Length < _array.Length) |
||||
|
{ |
||||
|
Array.Copy(_array, newArray, _size); |
||||
|
ReturnArray(replaceWith: newArray); |
||||
|
_version++; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// The array from the pool wasn't any smaller than the one we already had,
|
||||
|
// (we can only control minimum size) so return it and do nothing.
|
||||
|
// If we create an exact-sized array not from the pool, we'll
|
||||
|
// get an exception when returning it to the pool.
|
||||
|
_pool.Return(newArray); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the top object on the stack without removing it. If the stack
|
||||
|
/// is empty, Peek throws an InvalidOperationException.
|
||||
|
/// </summary>
|
||||
|
public T Peek() |
||||
|
{ |
||||
|
int size = _size - 1; |
||||
|
T[] array = _array; |
||||
|
|
||||
|
if ((uint)size >= (uint)array.Length) |
||||
|
{ |
||||
|
ThrowForEmptyStack(); |
||||
|
} |
||||
|
|
||||
|
return array[size]; |
||||
|
} |
||||
|
|
||||
|
public bool TryPeek(out T result) |
||||
|
{ |
||||
|
int size = _size - 1; |
||||
|
T[] array = _array; |
||||
|
|
||||
|
if ((uint)size >= (uint)array.Length) |
||||
|
{ |
||||
|
result = default; |
||||
|
return false; |
||||
|
} |
||||
|
result = array[size]; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Pops an item from the top of the stack. If the stack is empty, Pop
|
||||
|
/// throws an InvalidOperationException.
|
||||
|
/// </summary>
|
||||
|
public T Pop() |
||||
|
{ |
||||
|
int size = _size - 1; |
||||
|
T[] array = _array; |
||||
|
|
||||
|
// if (_size == 0) is equivalent to if (size == -1), and this case
|
||||
|
// is covered with (uint)size, thus allowing bounds check elimination
|
||||
|
// https://github.com/dotnet/coreclr/pull/9773
|
||||
|
if ((uint)size >= (uint)array.Length) |
||||
|
{ |
||||
|
ThrowForEmptyStack(); |
||||
|
} |
||||
|
|
||||
|
_version++; |
||||
|
_size = size; |
||||
|
T item = array[size]; |
||||
|
if (_clearOnFree) |
||||
|
{ |
||||
|
array[size] = default; // Free memory quicker.
|
||||
|
} |
||||
|
return item; |
||||
|
} |
||||
|
|
||||
|
public bool TryPop(out T result) |
||||
|
{ |
||||
|
int size = _size - 1; |
||||
|
T[] array = _array; |
||||
|
|
||||
|
if ((uint)size >= (uint)array.Length) |
||||
|
{ |
||||
|
result = default; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
_version++; |
||||
|
_size = size; |
||||
|
result = array[size]; |
||||
|
if (_clearOnFree) |
||||
|
{ |
||||
|
array[size] = default; // Free memory quicker.
|
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Pushes an item to the top of the stack.
|
||||
|
/// </summary>
|
||||
|
public void Push(T item) |
||||
|
{ |
||||
|
int size = _size; |
||||
|
T[] array = _array; |
||||
|
|
||||
|
if ((uint)size < (uint)array.Length) |
||||
|
{ |
||||
|
array[size] = item; |
||||
|
_version++; |
||||
|
_size = size + 1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
PushWithResize(item); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Non-inline from Stack.Push to improve its code quality as uncommon path
|
||||
|
[MethodImpl(MethodImplOptions.NoInlining)] |
||||
|
private void PushWithResize(T item) |
||||
|
{ |
||||
|
var newArray = _pool.Rent((_array.Length == 0) ? DefaultCapacity : 2 * _array.Length); |
||||
|
Array.Copy(_array, newArray, _size); |
||||
|
ReturnArray(replaceWith: newArray); |
||||
|
_array[_size] = item; |
||||
|
_version++; |
||||
|
_size++; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Copies the Stack to an array, in the same order Pop would return the items.
|
||||
|
/// </summary>
|
||||
|
public T[] ToArray() |
||||
|
{ |
||||
|
if (_size == 0) |
||||
|
return Array.Empty<T>(); |
||||
|
|
||||
|
T[] objArray = new T[_size]; |
||||
|
int i = 0; |
||||
|
while (i < _size) |
||||
|
{ |
||||
|
objArray[i] = _array[_size - i - 1]; |
||||
|
i++; |
||||
|
} |
||||
|
return objArray; |
||||
|
} |
||||
|
|
||||
|
private void ThrowForEmptyStack() |
||||
|
{ |
||||
|
Debug.Assert(_size == 0); |
||||
|
throw new InvalidOperationException("Stack was empty."); |
||||
|
} |
||||
|
|
||||
|
private void ReturnArray(T[] replaceWith = null) |
||||
|
{ |
||||
|
if (_array?.Length > 0) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_pool.Return(_array, clearArray: _clearOnFree); |
||||
|
} |
||||
|
catch (ArgumentException) |
||||
|
{ |
||||
|
// oh well, the array pool didn't like our array
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!(replaceWith is null)) |
||||
|
{ |
||||
|
_array = replaceWith; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static bool ShouldClear(ClearMode mode) |
||||
|
{ |
||||
|
#if NETCOREAPP2_1
|
||||
|
return mode == ClearMode.Always |
||||
|
|| (mode == ClearMode.Auto && RuntimeHelpers.IsReferenceOrContainsReferences<T>()); |
||||
|
#else
|
||||
|
return mode != ClearMode.Never; |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
ReturnArray(replaceWith: Array.Empty<T>()); |
||||
|
_size = 0; |
||||
|
_version++; |
||||
|
} |
||||
|
|
||||
|
void IDeserializationCallback.OnDeserialization(object sender) |
||||
|
{ |
||||
|
// We can't serialize array pools, so deserialized PooledStacks will
|
||||
|
// have to use the shared pool, even if they were using a custom pool
|
||||
|
// before serialization.
|
||||
|
_pool = ArrayPool<T>.Shared; |
||||
|
} |
||||
|
|
||||
|
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")] |
||||
|
public struct Enumerator : IEnumerator<T>, IEnumerator |
||||
|
{ |
||||
|
private readonly PooledStack<T> _stack; |
||||
|
private readonly int _version; |
||||
|
private int _index; |
||||
|
private T _currentElement; |
||||
|
|
||||
|
internal Enumerator(PooledStack<T> stack) |
||||
|
{ |
||||
|
_stack = stack; |
||||
|
_version = stack._version; |
||||
|
_index = -2; |
||||
|
_currentElement = default; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_index = -1; |
||||
|
} |
||||
|
|
||||
|
public bool MoveNext() |
||||
|
{ |
||||
|
bool retval; |
||||
|
if (_version != _stack._version) |
||||
|
throw new InvalidOperationException("Collection was modified during enumeration."); |
||||
|
if (_index == -2) |
||||
|
{ // First call to enumerator.
|
||||
|
_index = _stack._size - 1; |
||||
|
retval = (_index >= 0); |
||||
|
if (retval) |
||||
|
_currentElement = _stack._array[_index]; |
||||
|
return retval; |
||||
|
} |
||||
|
if (_index == -1) |
||||
|
{ // End of enumeration.
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
retval = (--_index >= 0); |
||||
|
if (retval) |
||||
|
_currentElement = _stack._array[_index]; |
||||
|
else |
||||
|
_currentElement = default; |
||||
|
return retval; |
||||
|
} |
||||
|
|
||||
|
public T Current |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_index < 0) |
||||
|
ThrowEnumerationNotStartedOrEnded(); |
||||
|
return _currentElement; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ThrowEnumerationNotStartedOrEnded() |
||||
|
{ |
||||
|
Debug.Assert(_index == -1 || _index == -2); |
||||
|
throw new InvalidOperationException(_index == -2 ? "Enumeration was not started." : "Enumeration has ended."); |
||||
|
} |
||||
|
|
||||
|
object IEnumerator.Current |
||||
|
{ |
||||
|
get { return Current; } |
||||
|
} |
||||
|
|
||||
|
void IEnumerator.Reset() |
||||
|
{ |
||||
|
if (_version != _stack._version) |
||||
|
throw new InvalidOperationException("Collection was modified during enumeration."); |
||||
|
_index = -2; |
||||
|
_currentElement = default; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
// 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.Diagnostics; |
||||
|
|
||||
|
namespace Avalonia.Collections.Pooled |
||||
|
{ |
||||
|
internal sealed class StackDebugView<T> |
||||
|
{ |
||||
|
private readonly PooledStack<T> _stack; |
||||
|
|
||||
|
public StackDebugView(PooledStack<T> stack) |
||||
|
{ |
||||
|
_stack = stack ?? throw new ArgumentNullException(nameof(stack)); |
||||
|
} |
||||
|
|
||||
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] |
||||
|
public T[] Items |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _stack.ToArray(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,691 @@ |
|||||
|
// 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.
|
||||
|
|
||||
|
|
||||
|
// This file defines an internal class used to throw exceptions in BCL code.
|
||||
|
// The main purpose is to reduce code size.
|
||||
|
//
|
||||
|
// The old way to throw an exception generates quite a lot IL code and assembly code.
|
||||
|
// Following is an example:
|
||||
|
// C# source
|
||||
|
// throw new ArgumentNullException(nameof(key), SR.ArgumentNull_Key);
|
||||
|
// IL code:
|
||||
|
// IL_0003: ldstr "key"
|
||||
|
// IL_0008: ldstr "ArgumentNull_Key"
|
||||
|
// IL_000d: call string System.Environment::GetResourceString(string)
|
||||
|
// IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string)
|
||||
|
// IL_0017: throw
|
||||
|
// which is 21bytes in IL.
|
||||
|
//
|
||||
|
// So we want to get rid of the ldstr and call to Environment.GetResource in IL.
|
||||
|
// In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the
|
||||
|
// argument name and resource name in a small integer. The source code will be changed to
|
||||
|
// ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key);
|
||||
|
//
|
||||
|
// The IL code will be 7 bytes.
|
||||
|
// IL_0008: ldc.i4.4
|
||||
|
// IL_0009: ldc.i4.4
|
||||
|
// IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument)
|
||||
|
// IL_000f: ldarg.0
|
||||
|
//
|
||||
|
// This will also reduce the Jitted code size a lot.
|
||||
|
//
|
||||
|
// It is very important we do this for generic classes because we can easily generate the same code
|
||||
|
// multiple times for different instantiation.
|
||||
|
//
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Serialization; |
||||
|
|
||||
|
namespace Avalonia.Collections.Pooled |
||||
|
{ |
||||
|
internal static class ThrowHelper |
||||
|
{ |
||||
|
internal static void ThrowArrayTypeMismatchException() |
||||
|
{ |
||||
|
throw new ArrayTypeMismatchException(); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowIndexOutOfRangeException() |
||||
|
{ |
||||
|
throw new IndexOutOfRangeException(); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentOutOfRangeException() |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException(); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentException_DestinationTooShort() |
||||
|
{ |
||||
|
throw new ArgumentException("Destination too short."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentException_OverlapAlignmentMismatch() |
||||
|
{ |
||||
|
throw new ArgumentException("Overlap alignment mismatch."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentOutOfRange_IndexException() |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(ExceptionArgument.index, |
||||
|
ExceptionResource.ArgumentOutOfRange_Index); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowIndexArgumentOutOfRange_NeedNonNegNumException() |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(ExceptionArgument.index, |
||||
|
ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowValueArgumentOutOfRange_NeedNonNegNumException() |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(ExceptionArgument.value, |
||||
|
ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum() |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(ExceptionArgument.length, |
||||
|
ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index() |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(ExceptionArgument.startIndex, |
||||
|
ExceptionResource.ArgumentOutOfRange_Index); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count() |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(ExceptionArgument.count, |
||||
|
ExceptionResource.ArgumentOutOfRange_Count); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowWrongKeyTypeArgumentException<T>(T key, Type targetType) |
||||
|
{ |
||||
|
// Generic key to move the boxing to the right hand side of throw
|
||||
|
throw GetWrongKeyTypeArgumentException((object)key, targetType); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowWrongValueTypeArgumentException<T>(T value, Type targetType) |
||||
|
{ |
||||
|
// Generic key to move the boxing to the right hand side of throw
|
||||
|
throw GetWrongValueTypeArgumentException((object)value, targetType); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentException GetAddingDuplicateWithKeyArgumentException(object key) |
||||
|
{ |
||||
|
return new ArgumentException($"Error adding duplicate with key: {key}."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowAddingDuplicateWithKeyArgumentException<T>(T key) |
||||
|
{ |
||||
|
// Generic key to move the boxing to the right hand side of throw
|
||||
|
throw GetAddingDuplicateWithKeyArgumentException((object)key); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowKeyNotFoundException<T>(T key) |
||||
|
{ |
||||
|
// Generic key to move the boxing to the right hand side of throw
|
||||
|
throw GetKeyNotFoundException((object)key); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw GetArgumentException(resource); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentException(ExceptionResource resource, ExceptionArgument argument) |
||||
|
{ |
||||
|
throw GetArgumentException(resource, argument); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentNullException GetArgumentNullException(ExceptionArgument argument) |
||||
|
{ |
||||
|
return new ArgumentNullException(GetArgumentName(argument)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentNullException(ExceptionArgument argument) |
||||
|
{ |
||||
|
throw GetArgumentNullException(argument); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentNullException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw new ArgumentNullException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentNullException(ExceptionArgument argument, ExceptionResource resource) |
||||
|
{ |
||||
|
throw new ArgumentNullException(GetArgumentName(argument), GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument) |
||||
|
{ |
||||
|
throw new ArgumentOutOfRangeException(GetArgumentName(argument)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(argument, resource); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, int paramNumber, ExceptionResource resource) |
||||
|
{ |
||||
|
throw GetArgumentOutOfRangeException(argument, paramNumber, resource); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw GetInvalidOperationException(resource); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException(ExceptionResource resource, Exception e) |
||||
|
{ |
||||
|
throw new InvalidOperationException(GetResourceString(resource), e); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowSerializationException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw new SerializationException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowSecurityException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw new System.Security.SecurityException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowRankException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw new RankException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowNotSupportedException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw new NotSupportedException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowUnauthorizedAccessException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw new UnauthorizedAccessException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowObjectDisposedException(string objectName, ExceptionResource resource) |
||||
|
{ |
||||
|
throw new ObjectDisposedException(objectName, GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowObjectDisposedException(ExceptionResource resource) |
||||
|
{ |
||||
|
throw new ObjectDisposedException(null, GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowNotSupportedException() |
||||
|
{ |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowAggregateException(List<Exception> exceptions) |
||||
|
{ |
||||
|
throw new AggregateException(exceptions); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowOutOfMemoryException() |
||||
|
{ |
||||
|
throw new OutOfMemoryException(); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowArgumentException_Argument_InvalidArrayType() |
||||
|
{ |
||||
|
throw new ArgumentException("Invalid array type."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_InvalidOperation_EnumNotStarted() |
||||
|
{ |
||||
|
throw new InvalidOperationException("Enumeration has not started."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_InvalidOperation_EnumEnded() |
||||
|
{ |
||||
|
throw new InvalidOperationException("Enumeration has ended."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_EnumCurrent(int index) |
||||
|
{ |
||||
|
throw GetInvalidOperationException_EnumCurrent(index); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() |
||||
|
{ |
||||
|
throw new InvalidOperationException("Collection was modified during enumeration."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen() |
||||
|
{ |
||||
|
throw new InvalidOperationException("Invalid enumerator state: enumeration cannot proceed."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_InvalidOperation_NoValue() |
||||
|
{ |
||||
|
throw new InvalidOperationException("No value provided."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSupported() |
||||
|
{ |
||||
|
throw new InvalidOperationException("Concurrent operations are not supported."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowInvalidOperationException_HandleIsNotInitialized() |
||||
|
{ |
||||
|
throw new InvalidOperationException("Handle is not initialized."); |
||||
|
} |
||||
|
|
||||
|
internal static void ThrowFormatException_BadFormatSpecifier() |
||||
|
{ |
||||
|
throw new FormatException("Bad format specifier."); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentException GetArgumentException(ExceptionResource resource) |
||||
|
{ |
||||
|
return new ArgumentException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
private static InvalidOperationException GetInvalidOperationException(ExceptionResource resource) |
||||
|
{ |
||||
|
return new InvalidOperationException(GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentException GetWrongKeyTypeArgumentException(object key, Type targetType) |
||||
|
{ |
||||
|
return new ArgumentException($"Wrong key type. Expected {targetType}, got: '{key}'.", nameof(key)); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentException GetWrongValueTypeArgumentException(object value, Type targetType) |
||||
|
{ |
||||
|
return new ArgumentException($"Wrong value type. Expected {targetType}, got: '{value}'.", nameof(value)); |
||||
|
} |
||||
|
|
||||
|
private static KeyNotFoundException GetKeyNotFoundException(object key) |
||||
|
{ |
||||
|
return new KeyNotFoundException($"Key not found: {key}"); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource) |
||||
|
{ |
||||
|
return new ArgumentOutOfRangeException(GetArgumentName(argument), GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentException GetArgumentException(ExceptionResource resource, ExceptionArgument argument) |
||||
|
{ |
||||
|
return new ArgumentException(GetResourceString(resource), GetArgumentName(argument)); |
||||
|
} |
||||
|
|
||||
|
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(ExceptionArgument argument, int paramNumber, ExceptionResource resource) |
||||
|
{ |
||||
|
return new ArgumentOutOfRangeException(GetArgumentName(argument) + "[" + paramNumber.ToString() + "]", GetResourceString(resource)); |
||||
|
} |
||||
|
|
||||
|
private static InvalidOperationException GetInvalidOperationException_EnumCurrent(int index) |
||||
|
{ |
||||
|
return new InvalidOperationException( |
||||
|
index < 0 ? |
||||
|
"Enumeration has not started" : |
||||
|
"Enumeration has ended"); |
||||
|
} |
||||
|
|
||||
|
// Allow nulls for reference types and Nullable<U>, but not for value types.
|
||||
|
// Aggressively inline so the jit evaluates the if in place and either drops the call altogether
|
||||
|
// Or just leaves null test and call to the Non-returning ThrowHelper.ThrowArgumentNullException
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
internal static void IfNullAndNullsAreIllegalThenThrow<T>(object value, ExceptionArgument argName) |
||||
|
{ |
||||
|
// Note that default(T) is not equal to null for value types except when T is Nullable<U>.
|
||||
|
if (!(default(T) == null) && value == null) |
||||
|
ThrowHelper.ThrowArgumentNullException(argName); |
||||
|
} |
||||
|
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
internal static void ThrowForUnsupportedVectorBaseType<T>() where T : struct |
||||
|
{ |
||||
|
if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) && |
||||
|
typeof(T) != typeof(short) && typeof(T) != typeof(ushort) && |
||||
|
typeof(T) != typeof(int) && typeof(T) != typeof(uint) && |
||||
|
typeof(T) != typeof(long) && typeof(T) != typeof(ulong) && |
||||
|
typeof(T) != typeof(float) && typeof(T) != typeof(double)) |
||||
|
{ |
||||
|
ThrowNotSupportedException(ExceptionResource.Arg_TypeNotSupported); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#if false // Reflection-based implementation does not work for CoreRT/ProjectN
|
||||
|
// This function will convert an ExceptionArgument enum value to the argument name string.
|
||||
|
[MethodImpl(MethodImplOptions.NoInlining)] |
||||
|
private static string GetArgumentName(ExceptionArgument argument) |
||||
|
{ |
||||
|
Debug.Assert(Enum.IsDefined(typeof(ExceptionArgument), argument), |
||||
|
"The enum value is not defined, please check the ExceptionArgument Enum."); |
||||
|
|
||||
|
return argument.ToString(); |
||||
|
} |
||||
|
#endif
|
||||
|
|
||||
|
private static string GetArgumentName(ExceptionArgument argument) |
||||
|
{ |
||||
|
switch (argument) |
||||
|
{ |
||||
|
case ExceptionArgument.obj: |
||||
|
return "obj"; |
||||
|
case ExceptionArgument.dictionary: |
||||
|
return "dictionary"; |
||||
|
case ExceptionArgument.array: |
||||
|
return "array"; |
||||
|
case ExceptionArgument.info: |
||||
|
return "info"; |
||||
|
case ExceptionArgument.key: |
||||
|
return "key"; |
||||
|
case ExceptionArgument.text: |
||||
|
return "text"; |
||||
|
case ExceptionArgument.values: |
||||
|
return "values"; |
||||
|
case ExceptionArgument.value: |
||||
|
return "value"; |
||||
|
case ExceptionArgument.startIndex: |
||||
|
return "startIndex"; |
||||
|
case ExceptionArgument.task: |
||||
|
return "task"; |
||||
|
case ExceptionArgument.ch: |
||||
|
return "ch"; |
||||
|
case ExceptionArgument.s: |
||||
|
return "s"; |
||||
|
case ExceptionArgument.input: |
||||
|
return "input"; |
||||
|
case ExceptionArgument.list: |
||||
|
return "list"; |
||||
|
case ExceptionArgument.index: |
||||
|
return "index"; |
||||
|
case ExceptionArgument.capacity: |
||||
|
return "capacity"; |
||||
|
case ExceptionArgument.collection: |
||||
|
return "collection"; |
||||
|
case ExceptionArgument.item: |
||||
|
return "item"; |
||||
|
case ExceptionArgument.converter: |
||||
|
return "converter"; |
||||
|
case ExceptionArgument.match: |
||||
|
return "match"; |
||||
|
case ExceptionArgument.count: |
||||
|
return "count"; |
||||
|
case ExceptionArgument.action: |
||||
|
return "action"; |
||||
|
case ExceptionArgument.comparison: |
||||
|
return "comparison"; |
||||
|
case ExceptionArgument.exceptions: |
||||
|
return "exceptions"; |
||||
|
case ExceptionArgument.exception: |
||||
|
return "exception"; |
||||
|
case ExceptionArgument.enumerable: |
||||
|
return "enumerable"; |
||||
|
case ExceptionArgument.start: |
||||
|
return "start"; |
||||
|
case ExceptionArgument.format: |
||||
|
return "format"; |
||||
|
case ExceptionArgument.culture: |
||||
|
return "culture"; |
||||
|
case ExceptionArgument.comparer: |
||||
|
return "comparer"; |
||||
|
case ExceptionArgument.comparable: |
||||
|
return "comparable"; |
||||
|
case ExceptionArgument.source: |
||||
|
return "source"; |
||||
|
case ExceptionArgument.state: |
||||
|
return "state"; |
||||
|
case ExceptionArgument.length: |
||||
|
return "length"; |
||||
|
case ExceptionArgument.comparisonType: |
||||
|
return "comparisonType"; |
||||
|
case ExceptionArgument.manager: |
||||
|
return "manager"; |
||||
|
case ExceptionArgument.sourceBytesToCopy: |
||||
|
return "sourceBytesToCopy"; |
||||
|
case ExceptionArgument.callBack: |
||||
|
return "callBack"; |
||||
|
case ExceptionArgument.creationOptions: |
||||
|
return "creationOptions"; |
||||
|
case ExceptionArgument.function: |
||||
|
return "function"; |
||||
|
case ExceptionArgument.delay: |
||||
|
return "delay"; |
||||
|
case ExceptionArgument.millisecondsDelay: |
||||
|
return "millisecondsDelay"; |
||||
|
case ExceptionArgument.millisecondsTimeout: |
||||
|
return "millisecondsTimeout"; |
||||
|
case ExceptionArgument.timeout: |
||||
|
return "timeout"; |
||||
|
case ExceptionArgument.type: |
||||
|
return "type"; |
||||
|
case ExceptionArgument.sourceIndex: |
||||
|
return "sourceIndex"; |
||||
|
case ExceptionArgument.sourceArray: |
||||
|
return "sourceArray"; |
||||
|
case ExceptionArgument.destinationIndex: |
||||
|
return "destinationIndex"; |
||||
|
case ExceptionArgument.destinationArray: |
||||
|
return "destinationArray"; |
||||
|
case ExceptionArgument.other: |
||||
|
return "other"; |
||||
|
case ExceptionArgument.newSize: |
||||
|
return "newSize"; |
||||
|
case ExceptionArgument.lowerBounds: |
||||
|
return "lowerBounds"; |
||||
|
case ExceptionArgument.lengths: |
||||
|
return "lengths"; |
||||
|
case ExceptionArgument.len: |
||||
|
return "len"; |
||||
|
case ExceptionArgument.keys: |
||||
|
return "keys"; |
||||
|
case ExceptionArgument.indices: |
||||
|
return "indices"; |
||||
|
case ExceptionArgument.endIndex: |
||||
|
return "endIndex"; |
||||
|
case ExceptionArgument.elementType: |
||||
|
return "elementType"; |
||||
|
case ExceptionArgument.arrayIndex: |
||||
|
return "arrayIndex"; |
||||
|
default: |
||||
|
Debug.Fail("The enum value is not defined, please check the ExceptionArgument Enum."); |
||||
|
return argument.ToString(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#if false // Reflection-based implementation does not work for CoreRT/ProjectN
|
||||
|
// This function will convert an ExceptionResource enum value to the resource string.
|
||||
|
[MethodImpl(MethodImplOptions.NoInlining)] |
||||
|
private static string GetResourceString(ExceptionResource resource) |
||||
|
{ |
||||
|
Debug.Assert(Enum.IsDefined(typeof(ExceptionResource), resource), |
||||
|
"The enum value is not defined, please check the ExceptionResource Enum."); |
||||
|
|
||||
|
return SR.GetResourceString(resource.ToString()); |
||||
|
} |
||||
|
#endif
|
||||
|
|
||||
|
private static string GetResourceString(ExceptionResource resource) |
||||
|
{ |
||||
|
switch (resource) |
||||
|
{ |
||||
|
case ExceptionResource.ArgumentOutOfRange_Index: |
||||
|
return "Argument 'index' was out of the range of valid values."; |
||||
|
case ExceptionResource.ArgumentOutOfRange_Count: |
||||
|
return "Argument 'count' was out of the range of valid values."; |
||||
|
case ExceptionResource.Arg_ArrayPlusOffTooSmall: |
||||
|
return "Array plus offset too small."; |
||||
|
case ExceptionResource.NotSupported_ReadOnlyCollection: |
||||
|
return "This operation is not supported on a read-only collection."; |
||||
|
case ExceptionResource.Arg_RankMultiDimNotSupported: |
||||
|
return "Multi-dimensional arrays are not supported."; |
||||
|
case ExceptionResource.Arg_NonZeroLowerBound: |
||||
|
return "Arrays with a non-zero lower bound are not supported."; |
||||
|
case ExceptionResource.ArgumentOutOfRange_ListInsert: |
||||
|
return "Insertion index was out of the range of valid values."; |
||||
|
case ExceptionResource.ArgumentOutOfRange_NeedNonNegNum: |
||||
|
return "The number must be non-negative."; |
||||
|
case ExceptionResource.ArgumentOutOfRange_SmallCapacity: |
||||
|
return "The capacity cannot be set below the current Count."; |
||||
|
case ExceptionResource.Argument_InvalidOffLen: |
||||
|
return "Invalid offset length."; |
||||
|
case ExceptionResource.ArgumentOutOfRange_BiggerThanCollection: |
||||
|
return "The given value was larger than the size of the collection."; |
||||
|
case ExceptionResource.Serialization_MissingKeys: |
||||
|
return "Serialization error: missing keys."; |
||||
|
case ExceptionResource.Serialization_NullKey: |
||||
|
return "Serialization error: null key."; |
||||
|
case ExceptionResource.NotSupported_KeyCollectionSet: |
||||
|
return "The KeyCollection does not support modification."; |
||||
|
case ExceptionResource.NotSupported_ValueCollectionSet: |
||||
|
return "The ValueCollection does not support modification."; |
||||
|
case ExceptionResource.InvalidOperation_NullArray: |
||||
|
return "Null arrays are not supported."; |
||||
|
case ExceptionResource.InvalidOperation_HSCapacityOverflow: |
||||
|
return "Set hash capacity overflow. Cannot increase size."; |
||||
|
case ExceptionResource.NotSupported_StringComparison: |
||||
|
return "String comparison not supported."; |
||||
|
case ExceptionResource.ConcurrentCollection_SyncRoot_NotSupported: |
||||
|
return "SyncRoot not supported."; |
||||
|
case ExceptionResource.ArgumentException_OtherNotArrayOfCorrectLength: |
||||
|
return "The other array is not of the correct length."; |
||||
|
case ExceptionResource.ArgumentOutOfRange_EndIndexStartIndex: |
||||
|
return "The end index does not come after the start index."; |
||||
|
case ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported: |
||||
|
return "Huge arrays are not supported."; |
||||
|
case ExceptionResource.Argument_AddingDuplicate: |
||||
|
return "Duplicate item added."; |
||||
|
case ExceptionResource.Argument_InvalidArgumentForComparison: |
||||
|
return "Invalid argument for comparison."; |
||||
|
case ExceptionResource.Arg_LowerBoundsMustMatch: |
||||
|
return "Array lower bounds must match."; |
||||
|
case ExceptionResource.Arg_MustBeType: |
||||
|
return "Argument must be of type: "; |
||||
|
case ExceptionResource.InvalidOperation_IComparerFailed: |
||||
|
return "IComparer failed."; |
||||
|
case ExceptionResource.NotSupported_FixedSizeCollection: |
||||
|
return "This operation is not suppored on a fixed-size collection."; |
||||
|
case ExceptionResource.Rank_MultiDimNotSupported: |
||||
|
return "Multi-dimensional arrays are not supported."; |
||||
|
case ExceptionResource.Arg_TypeNotSupported: |
||||
|
return "Type not supported."; |
||||
|
default: |
||||
|
Debug.Assert(false, |
||||
|
"The enum value is not defined, please check the ExceptionResource Enum."); |
||||
|
return resource.ToString(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//
|
||||
|
// The convention for this enum is using the argument name as the enum name
|
||||
|
//
|
||||
|
internal enum ExceptionArgument |
||||
|
{ |
||||
|
obj, |
||||
|
dictionary, |
||||
|
array, |
||||
|
info, |
||||
|
key, |
||||
|
text, |
||||
|
values, |
||||
|
value, |
||||
|
startIndex, |
||||
|
task, |
||||
|
ch, |
||||
|
s, |
||||
|
input, |
||||
|
list, |
||||
|
index, |
||||
|
capacity, |
||||
|
collection, |
||||
|
item, |
||||
|
converter, |
||||
|
match, |
||||
|
count, |
||||
|
action, |
||||
|
comparison, |
||||
|
exceptions, |
||||
|
exception, |
||||
|
enumerable, |
||||
|
start, |
||||
|
format, |
||||
|
culture, |
||||
|
comparer, |
||||
|
comparable, |
||||
|
source, |
||||
|
state, |
||||
|
length, |
||||
|
comparisonType, |
||||
|
manager, |
||||
|
sourceBytesToCopy, |
||||
|
callBack, |
||||
|
creationOptions, |
||||
|
function, |
||||
|
delay, |
||||
|
millisecondsDelay, |
||||
|
millisecondsTimeout, |
||||
|
timeout, |
||||
|
type, |
||||
|
sourceIndex, |
||||
|
sourceArray, |
||||
|
destinationIndex, |
||||
|
destinationArray, |
||||
|
other, |
||||
|
newSize, |
||||
|
lowerBounds, |
||||
|
lengths, |
||||
|
len, |
||||
|
keys, |
||||
|
indices, |
||||
|
endIndex, |
||||
|
elementType, |
||||
|
arrayIndex |
||||
|
} |
||||
|
|
||||
|
//
|
||||
|
// The convention for this enum is using the resource name as the enum name
|
||||
|
//
|
||||
|
internal enum ExceptionResource |
||||
|
{ |
||||
|
ArgumentOutOfRange_Index, |
||||
|
ArgumentOutOfRange_Count, |
||||
|
Arg_ArrayPlusOffTooSmall, |
||||
|
NotSupported_ReadOnlyCollection, |
||||
|
Arg_RankMultiDimNotSupported, |
||||
|
Arg_NonZeroLowerBound, |
||||
|
ArgumentOutOfRange_ListInsert, |
||||
|
ArgumentOutOfRange_NeedNonNegNum, |
||||
|
ArgumentOutOfRange_SmallCapacity, |
||||
|
Argument_InvalidOffLen, |
||||
|
ArgumentOutOfRange_BiggerThanCollection, |
||||
|
Serialization_MissingKeys, |
||||
|
Serialization_NullKey, |
||||
|
NotSupported_KeyCollectionSet, |
||||
|
NotSupported_ValueCollectionSet, |
||||
|
InvalidOperation_NullArray, |
||||
|
InvalidOperation_HSCapacityOverflow, |
||||
|
NotSupported_StringComparison, |
||||
|
ConcurrentCollection_SyncRoot_NotSupported, |
||||
|
ArgumentException_OtherNotArrayOfCorrectLength, |
||||
|
ArgumentOutOfRange_EndIndexStartIndex, |
||||
|
ArgumentOutOfRange_HugeArrayNotSupported, |
||||
|
Argument_AddingDuplicate, |
||||
|
Argument_InvalidArgumentForComparison, |
||||
|
Arg_LowerBoundsMustMatch, |
||||
|
Arg_MustBeType, |
||||
|
InvalidOperation_IComparerFailed, |
||||
|
NotSupported_FixedSizeCollection, |
||||
|
Rank_MultiDimNotSupported, |
||||
|
Arg_TypeNotSupported, |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue