Added FisheryLib.Collections.FishTable and Mono.Cecil extension methods

A bit of refactoring has been done too
This commit is contained in:
bbradson
2023-06-03 20:10:58 +02:00
parent 8fa92f15a6
commit cdf209eb02
72 changed files with 8145 additions and 2098 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -11,7 +11,6 @@
<loadBefore>
<li>Ludeon.RimWorld</li>
<li>me.samboycoding.betterloading</li>
<li>zetrith.prepatcher</li>
</loadBefore>
<loadAfter>
<li>brrainz.harmony</li>
+1
View File
@@ -1,3 +1,4 @@
# Fishery
Modding Library for RimWorld. Used by Performance Fish.
Useful functions include various abstractions around IL in FishTranspiler, to make writing of transpilers easier and more robust. A couple collections like Dictionary and Span have extension methods for fast and convenient lookups. Some functionalities of Microsoft's Community Toolkit are included, expanded upon and modified to better fit Unity. Equality includes generic extension methods to use in place of Equals on structs without memory allocations. Convert provides a faster, generic alternative to System.Convert.ToType. FisheryLib.Reflection provides a couple reflection helpers, Aliases has a methodof function intended to be utilized with `global using static Aliases` to directly retrieve method infos from static method groups.
Additionally this includes a fast and much more versatile System.Collections.Generic.Dictionary replacement in FisheryLib.Collections.FishTable, loosely based on Malte Skarupke's ska::bytell_hash_map, as well as several extensions and helpers for Mono.Cecil, which is utilized by Prepatcher.
+2 -1
View File
@@ -1,6 +1,6 @@
[*.cs]
file_header_template = Copyright (c) 2022 bradson\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at https://mozilla.org/MPL/2.0/.
file_header_template = Copyright (c) 2022 bradson\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v.2.0.If a copy of the MPL was not distributed with this\nfile, You can obtain one at https://mozilla.org/MPL/2.0/.
# CA2211: Non-constant fields should not be visible
dotnet_diagnostic.CA2211.severity = none
@@ -47,6 +47,7 @@ dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
[*.cs]
#### Naming styles ####
+3
View File
@@ -5,12 +5,15 @@
global using static FisheryLib.Aliases;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "alias")]
public static class Aliases
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once InconsistentNaming
public static MethodInfo methodof(Delegate method) => method.Method;
}
+151 -193
View File
@@ -1,18 +1,21 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using UnityEngine;
// ReSharper disable PossibleUnintendedReferenceComparison
namespace FisheryLib;
#region ArraySortHelper for single arrays
#pragma warning disable CS8500
internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
{
public static IArraySortHelper<T> Default { get; } = CreateArraySortHelper();
private static IArraySortHelper<T> CreateArraySortHelper()
=> typeof(IComparable<T>).IsAssignableFrom(typeof(T))
? (IArraySortHelper<T>)Activator.CreateInstance(typeof(GenericArraySortHelper<>).MakeGenericType(typeof(T)))
: new ArraySortHelper<T>();
? (IArraySortHelper<T>)Activator.CreateInstance(typeof(GenericArraySortHelper<>).MakeGenericType(typeof(T)))
: new ArraySortHelper<T>();
#region IArraySortHelper<T> Members
@@ -61,9 +64,7 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
Debug.Assert(i != j);
if (comparer(keys[i], keys[j]) > 0)
{
(keys[j], keys[i]) = (keys[i], keys[j]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -79,9 +80,7 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
Debug.Assert(comparer != null);
if (keys.Length > 1)
{
IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1), comparer!);
}
}
private static void IntroSort(Span<T> keys, int depthLimit, Comparison<T> comparer)
@@ -95,7 +94,6 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
{
if (partitionSize <= SortUtils.INTROSORT_SIZE_THRESHOLD)
{
if (partitionSize == 2)
{
SwapIfGreater(keys, comparer!, 0, 1);
@@ -119,6 +117,7 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
HeapSort(keys[..partitionSize], comparer!);
return;
}
depthLimit--;
var p = PickPivotAndPartition(keys[..partitionSize], comparer!);
@@ -141,12 +140,14 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
SwapIfGreater(keys, comparer!, 0, middle); // swap the low with the mid point
SwapIfGreater(keys, comparer!, 0, hi); // swap the low with the high
SwapIfGreater(keys, comparer!, 0, hi); // swap the low with the high
SwapIfGreater(keys, comparer!, middle, hi); // swap the middle with the high
var pivot = keys[middle];
Swap(keys, middle, hi - 1);
int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
int left = 0,
right = hi
- 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
while (left < right)
{
@@ -163,9 +164,7 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
// Put pivot in the right location.
if (left != hi - 1)
{
Swap(keys, left, hi - 1);
}
return left;
}
@@ -176,9 +175,7 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
var n = keys.Length;
for (var i = n >> 1; i >= 1; i--)
{
DownHeap(keys, i, n, comparer!);
}
for (var i = n; i > 1; i--)
{
@@ -196,9 +193,7 @@ internal sealed class ArraySortHelper<T> : IArraySortHelper<T>
{
var child = 2 * i;
if (child < n && comparer!(keys[child - 1], keys[child]) < 0)
{
child++;
}
if (!(comparer!(d, keys[child - 1]) < 0))
break;
@@ -241,29 +236,25 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
{
if (comparer == null || comparer == Comparer<T>.Default)
{
if (keys.Length > 1)
{
// For floating-point, do a pre-pass to move all NaNs to the beginning
// so that we can do an optimized comparison as part of the actual sort
// on the remainder of the values.
if (typeof(T) == typeof(double) ||
typeof(T) == typeof(float))
{
var nanLeft = SortUtils.MoveNansToFront(keys, default(Span<byte>));
if (nanLeft == keys.Length)
{
return;
}
keys = keys[nanLeft..];
}
if (keys.Length <= 1)
return;
IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1));
// For floating-point, do a pre-pass to move all NaNs to the beginning
// so that we can do an optimized comparison as part of the actual sort
// on the remainder of the values.
if (typeof(T) == typeof(double) || typeof(T) == typeof(float))
{
var nanLeft = SortUtils.MoveNansToFront(keys, default(Span<byte>));
if (nanLeft == keys.Length)
return;
keys = keys[nanLeft..];
}
IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1));
}
else
{
ArraySortHelper<T>.IntrospectiveSort(keys, comparer.Compare);
}
}
catch (IndexOutOfRangeException)
{
@@ -279,12 +270,10 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
/// <summary>Swaps the values in the two references if the first is greater than the second.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SwapIfGreater(ref T i, ref T j)
private static void SwapIfGreater([AllowNull] ref T i, ref T j)
{
if (i != null && GreaterThan(ref i, ref j))
{
Swap(ref i, ref j);
}
}
/// <summary>Swaps the values in the two references, regardless of whether the two references are the same.</summary>
@@ -333,6 +322,7 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
HeapSort(keys[..partitionSize]);
return;
}
depthLimit--;
var p = PickPivotAndPartition(keys[..partitionSize]);
@@ -343,7 +333,7 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
}
}
private static int PickPivotAndPartition(Span<T> keys)
private static unsafe int PickPivotAndPartition(Span<T> keys)
{
Debug.Assert(keys.Length >= SortUtils.INTROSORT_SIZE_THRESHOLD);
@@ -361,38 +351,39 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
Swap(ref middleRef, ref nextToLastRef);
// Walk the left and right pointers, swapping elements as necessary, until they cross.
// ReSharper disable once RedundantSuppressNullableWarningExpression
ref T leftRef = ref zeroRef!, rightRef = ref nextToLastRef!;
while (Unsafe.IsAddressLessThan(ref leftRef, ref rightRef))
{
if (pivot == null)
{
while (Unsafe.IsAddressLessThan(ref leftRef!, ref nextToLastRef) && (leftRef = ref Unsafe.Add(ref leftRef, 1)) == null)
while (Unsafe.IsAddressLessThan(ref leftRef!, ref nextToLastRef)
&& (leftRef = ref Unsafe.Add(ref leftRef, 1)) == null)
;
while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef) && (rightRef = ref Unsafe.Add(ref rightRef, -1)) != null)
while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef)
&& (rightRef = ref Unsafe.Add(ref rightRef, -1)) != null)
;
}
else
{
while (Unsafe.IsAddressLessThan(ref leftRef, ref nextToLastRef) && GreaterThan(ref pivot, ref leftRef = ref Unsafe.Add(ref leftRef, 1)))
while (Unsafe.IsAddressLessThan(ref leftRef, ref nextToLastRef)
&& GreaterThan(ref pivot, ref leftRef = ref Unsafe.Add(ref leftRef, 1)))
;
while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef) && LessThan(ref pivot, ref rightRef = ref Unsafe.Add(ref rightRef, -1)))
while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef)
&& LessThan(ref pivot, ref rightRef = ref Unsafe.Add(ref rightRef, -1)))
;
}
if (!Unsafe.IsAddressLessThan(ref leftRef, ref rightRef!))
{
break;
}
Swap(ref leftRef, ref rightRef);
}
// Put the pivot in the correct location.
if (!Unsafe.AreSame(ref leftRef, ref nextToLastRef))
{
Swap(ref leftRef, ref nextToLastRef);
}
return (int)((nint)Unsafe.ByteOffset(ref zeroRef, ref leftRef) / Unsafe.SizeOf<T>());
return (int)((nint)Unsafe.ByteOffset(ref zeroRef, ref leftRef) / sizeof(T));
}
private static void HeapSort(Span<T> keys)
@@ -401,9 +392,7 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
var n = keys.Length;
for (var i = n >> 1; i >= 1; i--)
{
DownHeap(keys, i, n);
}
for (var i = n; i > 1; i--)
{
@@ -419,9 +408,7 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
{
var child = 2 * i;
if (child < n && (keys[child - 1] == null || LessThan(ref keys[child - 1], ref keys[child])))
{
child++;
}
if (keys[child - 1] == null || !LessThan(ref d, ref keys[child - 1]))
break;
@@ -442,7 +429,8 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
var j = i;
while (j >= 0 && (t == null || LessThan(ref t, ref Unsafe.Add(ref MemoryMarshal.GetReference(keys), j))))
{
Unsafe.Add(ref MemoryMarshal.GetReference(keys), j + 1) = Unsafe.Add(ref MemoryMarshal.GetReference(keys), j);
Unsafe.Add(ref MemoryMarshal.GetReference(keys), j + 1)
= Unsafe.Add(ref MemoryMarshal.GetReference(keys), j);
j--;
}
@@ -460,58 +448,34 @@ internal sealed class GenericArraySortHelper<T> : IArraySortHelper<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call
private static bool LessThan(ref T left, ref T right)
=> typeof(T) == typeof(byte)
? (byte)(object)left < (byte)(object)right
: typeof(T) == typeof(sbyte)
? (sbyte)(object)left < (sbyte)(object)right
: typeof(T) == typeof(ushort)
? (ushort)(object)left < (ushort)(object)right
: typeof(T) == typeof(short)
? (short)(object)left < (short)(object)right
: typeof(T) == typeof(uint)
? (uint)(object)left < (uint)(object)right
: typeof(T) == typeof(int)
? (int)(object)left < (int)(object)right
: typeof(T) == typeof(ulong)
? (ulong)(object)left < (ulong)(object)right
: typeof(T) == typeof(long)
? (long)(object)left < (long)(object)right
: typeof(T) == typeof(nuint)
? (nuint)(object)left < (nuint)(object)right
: typeof(T) == typeof(nint)
? (nint)(object)left < (nint)(object)right
: typeof(T) == typeof(float)
? (float)(object)left < (float)(object)right
: typeof(T) == typeof(double)
? (double)(object)left < (double)(object)right
=> typeof(T) == typeof(byte) ? (byte)(object)left < (byte)(object)right
: typeof(T) == typeof(sbyte) ? (sbyte)(object)left < (sbyte)(object)right
: typeof(T) == typeof(ushort) ? (ushort)(object)left < (ushort)(object)right
: typeof(T) == typeof(short) ? (short)(object)left < (short)(object)right
: typeof(T) == typeof(uint) ? (uint)(object)left < (uint)(object)right
: typeof(T) == typeof(int) ? (int)(object)left < (int)(object)right
: typeof(T) == typeof(ulong) ? (ulong)(object)left < (ulong)(object)right
: typeof(T) == typeof(long) ? (long)(object)left < (long)(object)right
: typeof(T) == typeof(nuint) ? (nuint)(object)left < (nuint)(object)right
: typeof(T) == typeof(nint) ? (nint)(object)left < (nint)(object)right
: typeof(T) == typeof(float) ? (float)(object)left < (float)(object)right
: typeof(T) == typeof(double) ? (double)(object)left < (double)(object)right
: left.CompareTo(right) < 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call
private static bool GreaterThan(ref T left, ref T right)
=> typeof(T) == typeof(byte)
? (byte)(object)left > (byte)(object)right
: typeof(T) == typeof(sbyte)
? (sbyte)(object)left > (sbyte)(object)right
: typeof(T) == typeof(ushort)
? (ushort)(object)left > (ushort)(object)right
: typeof(T) == typeof(short)
? (short)(object)left > (short)(object)right
: typeof(T) == typeof(uint)
? (uint)(object)left > (uint)(object)right
: typeof(T) == typeof(int)
? (int)(object)left > (int)(object)right
: typeof(T) == typeof(ulong)
? (ulong)(object)left > (ulong)(object)right
: typeof(T) == typeof(long)
? (long)(object)left > (long)(object)right
: typeof(T) == typeof(nuint)
? (nuint)(object)left > (nuint)(object)right
: typeof(T) == typeof(nint)
? (nint)(object)left > (nint)(object)right
: typeof(T) == typeof(float)
? (float)(object)left > (float)(object)right
: typeof(T) == typeof(double)
? (double)(object)left > (double)(object)right
=> typeof(T) == typeof(byte) ? (byte)(object)left > (byte)(object)right
: typeof(T) == typeof(sbyte) ? (sbyte)(object)left > (sbyte)(object)right
: typeof(T) == typeof(ushort) ? (ushort)(object)left > (ushort)(object)right
: typeof(T) == typeof(short) ? (short)(object)left > (short)(object)right
: typeof(T) == typeof(uint) ? (uint)(object)left > (uint)(object)right
: typeof(T) == typeof(int) ? (int)(object)left > (int)(object)right
: typeof(T) == typeof(ulong) ? (ulong)(object)left > (ulong)(object)right
: typeof(T) == typeof(long) ? (long)(object)left > (long)(object)right
: typeof(T) == typeof(nuint) ? (nuint)(object)left > (nuint)(object)right
: typeof(T) == typeof(nint) ? (nint)(object)left > (nint)(object)right
: typeof(T) == typeof(float) ? (float)(object)left > (float)(object)right
: typeof(T) == typeof(double) ? (double)(object)left > (double)(object)right
: left.CompareTo(right) > 0;
}
@@ -525,8 +489,9 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
private static IArraySortHelper<TKey, TValue> CreateArraySortHelper()
=> typeof(IComparable<TKey>).IsAssignableFrom(typeof(TKey))
? (IArraySortHelper<TKey, TValue>)Activator.CreateInstance(typeof(GenericArraySortHelper<,>).MakeGenericType(typeof(TKey), typeof(TValue)))
: new ArraySortHelper<TKey, TValue>();
? (IArraySortHelper<TKey, TValue>)Activator.CreateInstance(
typeof(GenericArraySortHelper<,>).MakeGenericType(typeof(TKey), typeof(TValue)))
: new ArraySortHelper<TKey, TValue>();
public void Sort(Span<TKey> keys, Span<TValue> values, IComparer<TKey>? comparer)
{
@@ -546,19 +511,20 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
}
}
private static void SwapIfGreaterWithValues(Span<TKey> keys, Span<TValue> values, IComparer<TKey> comparer, int i, int j)
private static void SwapIfGreaterWithValues(Span<TKey> keys, Span<TValue> values, IComparer<TKey> comparer, int i,
int j)
{
Debug.Assert(comparer != null);
Debug.Assert(0 <= i && i < keys.Length && i < values.Length);
Debug.Assert(0 <= j && j < keys.Length && j < values.Length);
Debug.Assert(i != j);
if (comparer!.Compare(keys[i], keys[j]) > 0)
{
(keys[j], keys[i]) = (keys[i], keys[j]);
if (comparer!.Compare(keys[i], keys[j]) <= 0)
return;
(values[j], values[i]) = (values[i], values[j]);
}
(keys[j], keys[i]) = (keys[i], keys[j]);
(values[j], values[i]) = (values[i], values[j]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -577,9 +543,7 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
Debug.Assert(keys.Length == values.Length);
if (keys.Length > 1)
{
IntroSort(keys, values, 2 * (BitOperations.Log2((uint)keys.Length) + 1), comparer!);
}
}
public static void IntroSort(Span<TKey> keys, Span<TValue> values, int depthLimit, IComparer<TKey> comparer)
@@ -594,7 +558,6 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
{
if (partitionSize <= SortUtils.INTROSORT_SIZE_THRESHOLD)
{
if (partitionSize == 2)
{
SwapIfGreaterWithValues(keys, values, comparer!, 0, 1);
@@ -618,6 +581,7 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
HeapSort(keys[..partitionSize], values[..partitionSize], comparer!);
return;
}
depthLimit--;
var p = PickPivotAndPartition(keys[..partitionSize], values[..partitionSize], comparer!);
@@ -640,12 +604,14 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
SwapIfGreaterWithValues(keys, values, comparer!, 0, middle); // swap the low with the mid point
SwapIfGreaterWithValues(keys, values, comparer!, 0, hi); // swap the low with the high
SwapIfGreaterWithValues(keys, values, comparer!, 0, hi); // swap the low with the high
SwapIfGreaterWithValues(keys, values, comparer!, middle, hi); // swap the middle with the high
var pivot = keys[middle];
Swap(keys, values, middle, hi - 1);
int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
int left = 0,
right = hi
- 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
while (left < right)
{
@@ -662,9 +628,7 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
// Put pivot in the right location.
if (left != hi - 1)
{
Swap(keys, values, left, hi - 1);
}
return left;
}
@@ -675,9 +639,7 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
var n = keys.Length;
for (var i = n >> 1; i >= 1; i--)
{
DownHeap(keys, values, i, n, comparer!);
}
for (var i = n; i > 1; i--)
{
@@ -697,9 +659,7 @@ internal sealed class ArraySortHelper<TKey, TValue> : IArraySortHelper<TKey, TVa
{
var child = 2 * i;
if (child < n && comparer!.Compare(keys[child - 1], keys[child]) < 0)
{
child++;
}
if (!(comparer!.Compare(d, keys[child - 1]) < 0))
break;
@@ -752,14 +712,12 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
// For floating-point, do a pre-pass to move all NaNs to the beginning
// so that we can do an optimized comparison as part of the actual sort
// on the remainder of the values.
if (typeof(TKey) == typeof(double) ||
typeof(TKey) == typeof(float))
if (typeof(TKey) == typeof(double) || typeof(TKey) == typeof(float))
{
var nanLeft = SortUtils.MoveNansToFront(keys, values);
if (nanLeft == keys.Length)
{
return;
}
keys = keys[nanLeft..];
values = values[nanLeft..];
}
@@ -768,9 +726,7 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
}
}
else
{
ArraySortHelper<TKey, TValue>.IntrospectiveSort(keys, values, comparer);
}
}
catch (IndexOutOfRangeException)
{
@@ -818,7 +774,6 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
{
if (partitionSize <= SortUtils.INTROSORT_SIZE_THRESHOLD)
{
if (partitionSize == 2)
{
SwapIfGreaterWithValues(keys, values, 0, 1);
@@ -842,6 +797,7 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
HeapSort(keys[..partitionSize], values[..partitionSize]);
return;
}
depthLimit--;
var p = PickPivotAndPartition(keys[..partitionSize], values[..partitionSize]);
@@ -863,18 +819,20 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
SwapIfGreaterWithValues(keys, values, 0, middle); // swap the low with the mid point
SwapIfGreaterWithValues(keys, values, 0, hi); // swap the low with the high
SwapIfGreaterWithValues(keys, values, 0, hi); // swap the low with the high
SwapIfGreaterWithValues(keys, values, middle, hi); // swap the middle with the high
var pivot = keys[middle];
Swap(keys, values, middle, hi - 1);
int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
int left = 0,
right = hi
- 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
while (left < right)
{
if (pivot == null)
{
while (left < (hi - 1) && keys[++left] == null)
while (left < hi - 1 && keys[++left] == null)
;
while (right > 0 && keys[--right] != null)
;
@@ -895,9 +853,7 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
// Put pivot in the right location.
if (left != hi - 1)
{
Swap(keys, values, left, hi - 1);
}
return left;
}
@@ -907,9 +863,7 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
var n = keys.Length;
for (var i = n >> 1; i >= 1; i--)
{
DownHeap(keys, values, i, n);
}
for (var i = n; i > 1; i--)
{
@@ -927,9 +881,7 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
{
var child = 2 * i;
if (child < n && (keys[child - 1] == null || LessThan(ref keys[child - 1], ref keys[child])))
{
child++;
}
if (keys[child - 1] == null || !LessThan(ref d, ref keys[child - 1]))
break;
@@ -973,58 +925,34 @@ internal sealed class GenericArraySortHelper<TKey, TValue> : IArraySortHelper<TK
[MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call
private static bool LessThan(ref TKey left, ref TKey right)
=> typeof(TKey) == typeof(byte)
? (byte)(object)left < (byte)(object)right
: typeof(TKey) == typeof(sbyte)
? (sbyte)(object)left < (sbyte)(object)right
: typeof(TKey) == typeof(ushort)
? (ushort)(object)left < (ushort)(object)right
: typeof(TKey) == typeof(short)
? (short)(object)left < (short)(object)right
: typeof(TKey) == typeof(uint)
? (uint)(object)left < (uint)(object)right
: typeof(TKey) == typeof(int)
? (int)(object)left < (int)(object)right
: typeof(TKey) == typeof(ulong)
? (ulong)(object)left < (ulong)(object)right
: typeof(TKey) == typeof(long)
? (long)(object)left < (long)(object)right
: typeof(TKey) == typeof(nuint)
? (nuint)(object)left < (nuint)(object)right
: typeof(TKey) == typeof(nint)
? (nint)(object)left < (nint)(object)right
: typeof(TKey) == typeof(float)
? (float)(object)left < (float)(object)right
: typeof(TKey) == typeof(double)
? (double)(object)left < (double)(object)right
=> typeof(TKey) == typeof(byte) ? (byte)(object)left < (byte)(object)right
: typeof(TKey) == typeof(sbyte) ? (sbyte)(object)left < (sbyte)(object)right
: typeof(TKey) == typeof(ushort) ? (ushort)(object)left < (ushort)(object)right
: typeof(TKey) == typeof(short) ? (short)(object)left < (short)(object)right
: typeof(TKey) == typeof(uint) ? (uint)(object)left < (uint)(object)right
: typeof(TKey) == typeof(int) ? (int)(object)left < (int)(object)right
: typeof(TKey) == typeof(ulong) ? (ulong)(object)left < (ulong)(object)right
: typeof(TKey) == typeof(long) ? (long)(object)left < (long)(object)right
: typeof(TKey) == typeof(nuint) ? (nuint)(object)left < (nuint)(object)right
: typeof(TKey) == typeof(nint) ? (nint)(object)left < (nint)(object)right
: typeof(TKey) == typeof(float) ? (float)(object)left < (float)(object)right
: typeof(TKey) == typeof(double) ? (double)(object)left < (double)(object)right
: left.CompareTo(right) < 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call
private static bool GreaterThan(ref TKey left, ref TKey right)
=> typeof(TKey) == typeof(byte)
? (byte)(object)left > (byte)(object)right
: typeof(TKey) == typeof(sbyte)
? (sbyte)(object)left > (sbyte)(object)right
: typeof(TKey) == typeof(ushort)
? (ushort)(object)left > (ushort)(object)right
: typeof(TKey) == typeof(short)
? (short)(object)left > (short)(object)right
: typeof(TKey) == typeof(uint)
? (uint)(object)left > (uint)(object)right
: typeof(TKey) == typeof(int)
? (int)(object)left > (int)(object)right
: typeof(TKey) == typeof(ulong)
? (ulong)(object)left > (ulong)(object)right
: typeof(TKey) == typeof(long)
? (long)(object)left > (long)(object)right
: typeof(TKey) == typeof(nuint)
? (nuint)(object)left > (nuint)(object)right
: typeof(TKey) == typeof(nint)
? (nint)(object)left > (nint)(object)right
: typeof(TKey) == typeof(float)
? (float)(object)left > (float)(object)right
: typeof(TKey) == typeof(double)
? (double)(object)left > (double)(object)right
=> typeof(TKey) == typeof(byte) ? (byte)(object)left > (byte)(object)right
: typeof(TKey) == typeof(sbyte) ? (sbyte)(object)left > (sbyte)(object)right
: typeof(TKey) == typeof(ushort) ? (ushort)(object)left > (ushort)(object)right
: typeof(TKey) == typeof(short) ? (short)(object)left > (short)(object)right
: typeof(TKey) == typeof(uint) ? (uint)(object)left > (uint)(object)right
: typeof(TKey) == typeof(int) ? (int)(object)left > (int)(object)right
: typeof(TKey) == typeof(ulong) ? (ulong)(object)left > (ulong)(object)right
: typeof(TKey) == typeof(long) ? (long)(object)left > (long)(object)right
: typeof(TKey) == typeof(nuint) ? (nuint)(object)left > (nuint)(object)right
: typeof(TKey) == typeof(nint) ? (nint)(object)left > (nint)(object)right
: typeof(TKey) == typeof(float) ? (float)(object)left > (float)(object)right
: typeof(TKey) == typeof(double) ? (double)(object)left > (double)(object)right
: left.CompareTo(right) > 0;
}
@@ -1041,15 +969,13 @@ internal static class SortUtils
for (var i = 0; i < keys.Length; i++)
{
if ((typeof(TKey) == typeof(double) && double.IsNaN((double)(object)keys[i])) ||
(typeof(TKey) == typeof(float) && float.IsNaN((float)(object)keys[i])))
if ((typeof(TKey) == typeof(double) && double.IsNaN((double)(object)keys[i]))
|| (typeof(TKey) == typeof(float) && float.IsNaN((float)(object)keys[i])))
{
(keys[i], keys[left]) = (keys[left], keys[i]);
if ((uint)i < (uint)values.Length) // check to see if we have values
{
(values[i], values[left]) = (values[left], values[i]);
}
left++;
}
@@ -1067,9 +993,12 @@ internal static class SortUtils
internal static void ThrowArgumentException_BadComparer(object? comparer)
=> throw new ArgumentException(string.Format(Arg_BogusIComparer, comparer));
internal static string InvalidOperation_IComparerFailed => "Failed to compare two elements in the array.";
internal static string InvalidOperation_IComparerFailed = "Failed to compare two elements in the array.";
internal static string Arg_BogusIComparer
=> "Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: '{0}'.";
= "Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not "
+ "compare equal to itself, or one value repeatedly compared to another value yields different results. "
+ "IComparer: '{0}'.";
}
internal interface IArraySortHelper<TKey, TValue>
@@ -1085,12 +1014,40 @@ internal interface IArraySortHelper<TKey>
internal static class BitOperations
{
private static ReadOnlySpan<byte> Log2DeBruijn
=> new byte[32]
=> new byte[]
{
00, 09, 01, 10, 13, 21, 02, 29,
11, 14, 16, 18, 22, 25, 03, 30,
08, 12, 20, 28, 15, 17, 24, 07,
19, 27, 23, 06, 26, 05, 04, 31
00,
09,
01,
10,
13,
21,
02,
29,
11,
14,
16,
18,
22,
25,
03,
30,
08,
12,
20,
28,
15,
17,
24,
07,
19,
27,
23,
06,
26,
05,
04,
31
};
/// <summary>
@@ -1161,3 +1118,4 @@ internal static class BitOperations
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27));
}
}
#pragma warning restore CS8500
+11 -9
View File
@@ -5,12 +5,14 @@
// https://github.com/CommunityToolkit/dotnet/blob/main/CommunityToolkit.HighPerformance/Extensions/BoolExtensions.cs
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
/// <summary>
/// Helpers for working with the <see cref="bool"/> type.
/// </summary>
[PublicAPI]
public static class BoolExtensions
{
/// <summary>
@@ -27,7 +29,7 @@ public static class BoolExtensions
// remove unwanted stack spills if the caller is using constant arguments. This is
// because taking the address of an argument can interfere with some of the flow
// analysis executed by the JIT, which can in some cases block constant propagation.
bool copy = flag;
var copy = flag;
return *(byte*)&copy;
}
@@ -49,10 +51,10 @@ public static class BoolExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe int ToBitwiseMask32(this bool flag)
{
bool copy = flag;
byte rangeFlag = *(byte*)&copy;
int negativeFlag = rangeFlag - 1;
int mask = ~negativeFlag;
var copy = flag;
var rangeFlag = *(byte*)&copy;
var negativeFlag = rangeFlag - 1;
var mask = ~negativeFlag;
return mask;
}
@@ -67,10 +69,10 @@ public static class BoolExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe long ToBitwiseMask64(this bool flag)
{
bool copy = flag;
byte rangeFlag = *(byte*)&copy;
long negativeFlag = (long)rangeFlag - 1;
long mask = ~negativeFlag;
var copy = flag;
var rangeFlag = *(byte*)&copy;
var negativeFlag = (long)rangeFlag - 1;
var mask = ~negativeFlag;
return mask;
}
+559
View File
@@ -0,0 +1,559 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.CompilerServices;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
using Verse;
using CallSite = Mono.Cecil.CallSite;
namespace FisheryLib.Cecil;
public static class CecilExtensions
{
public static MethodReference ImportReference(this ModuleDefinition module, Delegate method)
=> module.ImportReference(method.Method);
public static MethodReference MakeGenericMethod<T>(this MethodReference genericMethodDefinition,
ICollection<T> genericArguments) where T : TypeReference
{
if (genericArguments.Count != genericMethodDefinition.GenericParameters.Count)
ThrowForInvalidGenericArgumentCount(genericMethodDefinition, genericArguments);
var genericInstanceMethod = new GenericInstanceMethod(genericMethodDefinition);
foreach (var genericArgument in genericArguments)
genericInstanceMethod.GenericArguments.Add(genericArgument);
return genericInstanceMethod;
}
private static void ThrowForInvalidGenericArgumentCount<T>(IGenericParameterProvider genericParameterProvider,
ICollection<T> genericArguments) where T : TypeReference
=> throw new ArgumentException(
$"Invalid generic argument count for {genericParameterProvider}. Received {
genericArguments.Count}, must be {genericParameterProvider.GenericParameters.Count}");
public static MethodReference MakeGenericMethod<T>(this MethodReference genericMethodDefinition,
params T[] genericArguments) where T : TypeReference
=> MakeGenericMethod(genericMethodDefinition, (ICollection<T>)genericArguments);
public static MethodReference ImportMethodWithGenericArguments<T>(this ModuleDefinition module, MethodInfo method,
ICollection<T> genericArguments) where T : TypeReference
=> module.ImportReference(method.IsGenericMethodDefinition ? method : method.GetGenericMethodDefinition())
.MakeGenericMethod(genericArguments);
public static MethodReference ImportMethodWithGenericArguments<T>(this ModuleDefinition module, MethodInfo method,
params T[] genericArguments) where T : TypeReference
=> module.ImportMethodWithGenericArguments(method, (ICollection<T>)genericArguments);
public static MethodReference ImportMethodWithGenericArguments<T>(this ModuleDefinition module, Delegate method,
ICollection<T> genericArguments) where T : TypeReference
=> module.ImportMethodWithGenericArguments(method.Method, genericArguments);
public static MethodReference ImportMethodWithGenericArguments<T>(this ModuleDefinition module, Delegate method,
params T[] genericArguments) where T : TypeReference
=> module.ImportMethodWithGenericArguments(method.Method, (ICollection<T>)genericArguments);
public static TypeReference MakeGenericType<T>(this TypeReference genericTypeDefinition,
ICollection<T> genericArguments) where T : TypeReference
{
if (genericArguments.Count != genericTypeDefinition.GenericParameters.Count)
ThrowForInvalidGenericArgumentCount(genericTypeDefinition, genericArguments);
var genericInstanceType = new GenericInstanceType(genericTypeDefinition);
foreach (var genericArgument in genericArguments)
genericInstanceType.GenericArguments.Add(genericArgument);
return genericInstanceType;
}
public static TypeReference MakeGenericType<T>(this TypeReference genericTypeDefinition,
params T[] genericArguments) where T : TypeReference
=> MakeGenericType(genericTypeDefinition, (ICollection<T>)genericArguments);
public static TypeReference ImportTypeWithGenericArguments<T>(this ModuleDefinition module, Type type,
ICollection<T> genericArguments) where T : TypeReference
=> module.ImportReference(type.IsGenericTypeDefinition ? type : type.GetElementType())
.MakeGenericType(genericArguments);
public static TypeReference ImportTypeWithGenericArguments<T>(this ModuleDefinition module, Type type,
params T[] genericArguments) where T : TypeReference
=> module.ImportTypeWithGenericArguments(type, (ICollection<T>)genericArguments);
public static VariableDefinition DeclareLocal(this ILProcessor ilProcessor, Type localType)
{
var variableDefinition = new VariableDefinition(ilProcessor.GetModule().ImportReference(localType));
ilProcessor.Body.Variables.Add(variableDefinition);
return variableDefinition;
}
public static VariableDefinition DeclareLocal(this ILProcessor ilProcessor, TypeReference localType)
{
var variableDefinition = new VariableDefinition(localType);
ilProcessor.Body.Variables.Add(variableDefinition);
return variableDefinition;
}
public static void InsertAt(this ILProcessor ilProcessor, int index, Instruction instruction)
=> ilProcessor.instructions.Insert(index, instruction);
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, TypeReference type)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, type));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, Type type)
=> ilProcessor.InsertAt(index, opCode, ilProcessor.GetModule().ImportReference(type));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, CallSite site)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, site));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, MethodReference method)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, method));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, MethodBase method,
params Type[] genericArguments)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode,
ImportReferenceWithOptionalGenericArguments(method, ilProcessor.GetModule(), genericArguments)));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, Delegate method,
params Type[] genericArguments)
=> ilProcessor.InsertAt(index, opCode, method.Method, genericArguments);
private static MethodReference ImportReferenceWithOptionalGenericArguments(MethodBase method,
ModuleDefinition module, params Type[] genericArguments)
=> genericArguments.Length > 0
? module.ImportMethodWithGenericArguments((MethodInfo)method, genericArguments)
: module.ImportReference(method);
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, FieldReference field)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, field));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, FieldInfo field)
=> ilProcessor.InsertAt(index, opCode, ilProcessor.GetModule().ImportReference(field));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, string value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, sbyte value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, byte value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, int value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, long value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, nint value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, float value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, double value)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, value));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, Instruction target)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, target));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, Instruction[] targets)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, targets));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, VariableDefinition variable)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, variable));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, ParameterDefinition parameter)
=> ilProcessor.InsertAt(index, Instruction.Create(opCode, parameter));
public static void InsertAt(this ILProcessor ilProcessor, int index, OpCode opCode, object operand)
=> ilProcessor.InsertAt(index, CreateInstruction(opCode, operand));
public static void InsertAt(this ILProcessor ilProcessor, int index, object instruction)
{
var module = ilProcessor.GetModule();
ilProcessor.InsertAt(index, instruction switch
{
Instruction codeInstruction => codeInstruction,
OpCode opCode => Instruction.Create(opCode),
ITuple tuple => CreateInstruction(tuple is [OpCode opCode, _, ..]
? opCode
: ThrowArgumentExceptionForInvalidType<OpCode>(instruction),
tuple[1] switch
{
MethodBase => ImportMethodFromTuple(tuple, module),
FieldInfo fieldInfo => module.ImportReference(fieldInfo),
Type => ImportTypeFromTuple(tuple, module),
ParameterInfo parameterInfo => GetParameterDefinition(ilProcessor, parameterInfo),
Delegate @delegate => module.ImportReference(@delegate.Method),
_ => tuple[1]
}),
_ => ThrowArgumentExceptionForInvalidType<Instruction>(instruction)
});
}
private static ParameterDefinition GetParameterDefinition(ILProcessor ilProcessor, ParameterInfo parameterInfo)
{
var parameters = ilProcessor.Body.Method.Parameters;
foreach (var parameter in parameters)
{
if (parameter.Name == parameterInfo.Name)
return parameter;
}
return ThrowHelper.ThrowArgumentException<ParameterDefinition>($"Could not find parameter named {
parameterInfo.Name} in method {ilProcessor.Body.Method.FullName}");
}
private static MethodReference ImportMethodFromTuple(ITuple tuple, ModuleDefinition module)
{
var tupleLength = tuple.Length;
Guard.IsGreaterThanOrEqualTo(tupleLength, 2);
if (tupleLength == 2)
return module.ImportReference((MethodBase)tuple[1]);
var methodInfo = (MethodInfo)tuple[1];
switch (tuple[2])
{
case Type[] typeArray:
{
return module.ImportMethodWithGenericArguments(methodInfo, typeArray);
}
case Type:
{
var genericArguments = new Type[tupleLength - 2];
for (var i = 2; i < tupleLength; i++)
genericArguments[i - 2] = (Type)tuple[i];
return module.ImportMethodWithGenericArguments(methodInfo, genericArguments);
}
case Collection<GenericParameter> genericParameterCollection:
{
return module.ImportMethodWithGenericArguments(methodInfo, genericParameterCollection);
}
case Collection<TypeReference> typeReferenceCollection:
{
return module.ImportMethodWithGenericArguments(methodInfo, typeReferenceCollection);
}
case TypeReference:
{
var genericArguments = new TypeReference[tupleLength - 2];
for (var i = 2; i < tupleLength; i++)
genericArguments[i - 2] = (TypeReference)tuple[i];
return module.ImportMethodWithGenericArguments(methodInfo, genericArguments);
}
default:
{
return ThrowHelper.ThrowArgumentException<MethodReference>(
$"Tuple contains invalid type: {tuple[2].GetType().FullName}");
}
}
}
private static TypeReference ImportTypeFromTuple(ITuple tuple, ModuleDefinition module)
{
var tupleLength = tuple.Length;
Guard.IsGreaterThanOrEqualTo(tupleLength, 2);
if (tupleLength == 2)
return module.ImportReference((Type)tuple[1]);
var type = (Type)tuple[1];
switch (tuple[2])
{
case Type[] typeArray:
{
return module.ImportTypeWithGenericArguments(type, typeArray);
}
case Type:
{
var genericArguments = new Type[tupleLength - 2];
for (var i = 2; i < tupleLength; i++)
genericArguments[i - 2] = (Type)tuple[i];
return module.ImportTypeWithGenericArguments(type, genericArguments);
}
case Collection<GenericParameter> genericParameterCollection:
{
return module.ImportTypeWithGenericArguments(type, genericParameterCollection);
}
case Collection<TypeReference> typeReferenceCollection:
{
return module.ImportTypeWithGenericArguments(type, typeReferenceCollection);
}
case TypeReference:
{
var genericArguments = new TypeReference[tupleLength - 2];
for (var i = 2; i < tupleLength; i++)
genericArguments[i - 2] = (TypeReference)tuple[i];
return module.ImportTypeWithGenericArguments(type, genericArguments);
}
default:
{
return ThrowHelper.ThrowArgumentException<TypeReference>(
$"Tuple contains invalid type: {tuple[2].GetType().FullName}");
}
}
}
public static void InsertRange(this ILProcessor ilProcessor, int index, IEnumerable<Instruction> instructions)
{
foreach (var instruction in instructions)
ilProcessor.InsertAt(index++, instruction);
}
public static void InsertRange(this ILProcessor ilProcessor, int index, params Instruction[] instructions)
{
for (var i = 0; i < instructions.Length; i++)
ilProcessor.InsertAt(index++, instructions[i]);
}
public static void InsertRange(this ILProcessor ilProcessor, int index, IEnumerable<object> instructions)
{
foreach (var instruction in instructions)
ilProcessor.InsertAt(index++, instruction);
}
public static void InsertRange(this ILProcessor ilProcessor, int index, params object[] instructions)
{
for (var i = 0; i < instructions.Length; i++)
ilProcessor.InsertAt(index++, instructions[i]);
}
public static void ReplaceBodyWith(this ILProcessor ilProcessor, Delegate replacement)
=> ilProcessor.ReplaceBodyWith(replacement.Method);
public static void ReplaceBodyWith(this ILProcessor ilProcessor, MethodInfo replacement)
{
var oldBody = ilProcessor.Body;
var module = oldBody.Method.Module;
if (replacement.IsGenericMethod)
replacement = replacement.GetGenericMethodDefinition();
if (replacement.DeclaringType!.IsGenericType)
{
replacement = AccessTools.DeclaredMethod(replacement.DeclaringType.GetGenericTypeDefinition(),
replacement.Name, replacement.GetParameters().GetTypes());
}
var newMethodReference = module.ImportReference(replacement);
var newBody = newMethodReference.Resolve().Body;
oldBody.Instructions.Clear();
foreach (var instruction in newBody.instructions)
{
oldBody.Instructions.Add(new(instruction.OpCode, instruction.Operand switch
{
FieldReference field => module.ImportReference(field, newMethodReference),
MethodReference method => module.ImportReference(method, newMethodReference),
TypeReference type => module.ImportReference(type, newMethodReference),
_ => instruction.Operand
}));
}
oldBody.Variables.Clear();
foreach (var variable in newBody.Variables)
oldBody.Variables.Add(new(module.ImportReference(variable.VariableType, newMethodReference)));
oldBody.MaxStackSize = newBody.MaxStackSize;
oldBody.ExceptionHandlers.Clear();
foreach (var exceptionHandler in newBody.ExceptionHandlers)
{
var handler = new ExceptionHandler(exceptionHandler.HandlerType)
{
TryStart = exceptionHandler.TryStart,
TryEnd = exceptionHandler.TryEnd,
FilterStart = exceptionHandler.FilterStart,
HandlerStart = exceptionHandler.HandlerStart,
HandlerEnd = exceptionHandler.HandlerEnd
};
if (exceptionHandler.CatchType is { } catchType)
handler.CatchType = module.ImportReference(catchType, newMethodReference);
oldBody.ExceptionHandlers.Add(handler);
}
oldBody.Method.ImplAttributes = newBody.Method.ImplAttributes;
}
public static MethodDefinition GetMethod(this ILProcessor ilProcessor) => ilProcessor.Body.Method;
public static MethodDefinition? GetMethod(this TypeDefinition type, string name, Type?[]? parameters = null)
{
var methods = type.Methods;
MethodDefinition? foundMethod = null;
for (var i = 0; i < methods.Count; i++)
{
var method = methods[i];
if (method.Name != name
|| !TestForMatchingTypes(parameters, method.Parameters, static p => p.ParameterType.Name))
{
continue;
}
if (foundMethod != null)
ThrowForAmbiguousMatch(type, name, parameters);
else
foundMethod = method;
}
return foundMethod;
}
public static TypeDefinition? GetTypeDefinition(this ModuleDefinition module, Type type)
=> type.DeclaringType is { } declaringType
? module.GetTypeDefinition(declaringType)?.NestedTypes.GetNamed(type.Name)
: module.GetType(type.Namespace, type.Name);
private static void ThrowForAmbiguousMatch(TypeDefinition type, string name, Type?[]? parameters)
=> throw new AmbiguousMatchException($"Ambiguous match found for type: {type.FullName}, name: {
name}, parameters: {parameters.ToStringSafeEnumerable()}");
private static bool TestForMatchingTypes<T>(Type?[]? types, Collection<T>? testedTypes,
Func<T, string> typeNameGetter)
{
if (types is null)
return true;
if (testedTypes is null || testedTypes.Count != types.Length)
return false;
var typesNotMatching = false;
for (var i = 0; i < types.Length; i++)
{
var type = types[i];
if (type is null || type.IsGenericParameter)
continue;
if (typeNameGetter(testedTypes[i]) != type.Name)
typesNotMatching = true;
}
return !typesNotMatching;
}
public static ModuleDefinition GetModule(this ILProcessor ilProcessor) => ilProcessor.GetMethod().Module;
public static Collection<TypeReference>? TryGetGenericArguments(this MethodReference method)
=> method is GenericInstanceMethod instanceMethod
? instanceMethod.GenericArguments
: method.DeclaringType.TryGetGenericArguments();
public static Collection<TypeReference>? TryGetGenericArguments(this TypeReference type)
{
while (type != null)
{
if (type is GenericInstanceType instanceType)
return instanceType.GenericArguments;
type = type.DeclaringType;
}
return null;
}
public static Collection<TypeReference>? TryGetGenericArguments(this TypeReference type,
IGenericParameterProvider provider)
{
while (type != null)
{
if (type is GenericInstanceType instanceType)
return instanceType.GenericArguments.ConvertAll(g => g.TryGetGenericParameterType(provider) ?? g);
type = type.DeclaringType;
}
return null;
}
public static Collection<TypeReference>? TryGetGenericArguments(this IGenericParameterProvider provider)
=> provider switch
{
null => ThrowHelper.ThrowArgumentNullException<Collection<TypeReference>>(nameof(provider)),
MethodReference method => method.TryGetGenericArguments(),
TypeReference type => type.TryGetGenericArguments(),
_ => ThrowHelper.ThrowArgumentException<Collection<TypeReference>>(
$"Generic parameter provider has unsupported type: {provider.GetType()}")
};
public static TypeReference? TryGetGenericParameterType(this TypeReference typeReference,
IGenericParameterProvider provider)
=> typeReference is GenericParameter genericParameter
? TryGetUnderlyingType(genericParameter, provider)
: null;
public static TypeReference? TryGetUnderlyingType(GenericParameter genericParameter,
IGenericParameterProvider provider)
=> (genericParameter.Owner is TypeReference ownerType && provider is MemberReference providerMember
? providerMember.TryGetParentOfName(ownerType.Name)
: provider)?.TryGetGenericArguments()?[genericParameter.Position];
public static Collection<TOutput> ConvertAll<TInput, TOutput>(this Collection<TInput> collection,
Converter<TInput, TOutput> converter)
{
var result = new Collection<TOutput>(collection.Count);
for (var i = 0; i < collection.Count; i++)
result.Add(converter(collection[i])); // somehow throws OutOfRangeExceptions when using set_Item here
// Storing collection.Count in a var makes no difference
return result;
}
private static TypeReference? TryGetParentOfName(this MemberReference providerMember, string name)
{
var type = providerMember as TypeReference ?? providerMember.DeclaringType;
while (type != null)
{
if (type.Name == name)
return type;
type = type.DeclaringType;
}
return null;
}
private static T ThrowArgumentExceptionForInvalidType<T>(object instruction)
=> ThrowHelper.ThrowArgumentException<T>(nameof(instruction),
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
$"Argument is of invalid type: {instruction?.GetType().FullName ?? "NULL"}");
private static Instruction CreateInstruction(OpCode opCode, object operand)
=> operand switch
{
TypeReference type => Instruction.Create(opCode, type),
CallSite site => Instruction.Create(opCode, site),
MethodReference method => Instruction.Create(opCode, method),
FieldReference field => Instruction.Create(opCode, field),
string value => Instruction.Create(opCode, value),
sbyte value => Instruction.Create(opCode, value),
byte value => Instruction.Create(opCode, value),
int value => Instruction.Create(opCode, value),
long value => Instruction.Create(opCode, value),
float value => Instruction.Create(opCode, value),
double value => Instruction.Create(opCode, value),
nint value => Instruction.Create(opCode, value),
Instruction target => Instruction.Create(opCode, target),
Instruction[] targets => Instruction.Create(opCode, targets),
VariableDefinition variable => Instruction.Create(opCode, variable),
ParameterDefinition parameter => Instruction.Create(opCode, parameter),
_ => ThrowHelper.ThrowArgumentException<Instruction>(nameof(operand),
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
$"Operand for OpCode {opCode} is of invalid type: {operand?.GetType().FullName ?? "NULL"}")
};
}
@@ -0,0 +1,25 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v.2.0.If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using Mono.Cecil;
namespace FisheryLib.Cecil;
public static class CecilReflectionExtensions
{
public static MethodReference ImportMethodWithGenericArguments(this ModuleDefinition module, MethodInfo method,
params Type[] genericArguments)
=> module.ImportReference(method.IsGenericMethodDefinition ? method : method.GetGenericMethodDefinition())
.MakeGenericMethod(genericArguments.GetTypeReferences(module));
public static MethodReference ImportMethodWithGenericArguments(this ModuleDefinition module, Delegate method,
params Type[] genericArguments)
=> module.ImportMethodWithGenericArguments(method.Method, genericArguments);
public static TypeReference ImportTypeWithGenericArguments(this ModuleDefinition module, Type type,
params Type[] genericArguments)
=> module.ImportReference(type.IsGenericTypeDefinition ? type : type.GetElementType())
.MakeGenericType(genericArguments.GetTypeReferences(module));
}
@@ -0,0 +1,84 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
using Mono.Cecil;
using Mono.Collections.Generic;
namespace FisheryLib.Cecil;
[PublicAPI]
public static class ReflectionExtensions
{
public static Type[] GetTypes(this ParameterInfo[] parameterInfos)
=> Array.ConvertAll(parameterInfos, static p => p.ParameterType);
public static ParameterInfo? TryGetNamed(this ParameterInfo[] parameterInfos, string name)
=> TryGetNamed(parameterInfos, name, static p => p.Name);
public static T? TryGetNamed<T>(this T[] memberInfos, string name) where T : MemberInfo
=> TryGetNamed(memberInfos, name, static m => m.Name);
public static ParameterDefinition? TryGetNamed(this Collection<ParameterDefinition> parameterDefinitions,
string name)
=> TryGetNamed(parameterDefinitions, name, static p => p.Name);
public static T? TryGetNamed<T>(this Collection<T> memberReferences, string name) where T : MemberReference
=> TryGetNamed(memberReferences, name, static m => m.Name);
public static ParameterInfo GetNamed(this ParameterInfo[] parameterInfos, string name)
=> TryGetNamed(parameterInfos, name, static p => p.Name)
?? ThrowForMissingParameter<ParameterInfo>(name);
public static T GetNamed<T>(this T[] memberInfos, string name) where T : MemberInfo
=> TryGetNamed(memberInfos, name, static m => m.Name)
?? ThrowForMissingMember<T>(name);
public static ParameterDefinition GetNamed(this Collection<ParameterDefinition> parameterDefinitions,
string name)
=> TryGetNamed(parameterDefinitions, name, static p => p.Name)
?? ThrowForMissingParameter<ParameterDefinition>(name);
public static T GetNamed<T>(this Collection<T> memberReferences, string name) where T : MemberReference
=> TryGetNamed(memberReferences, name, static m => m.Name)
?? ThrowForMissingMember<T>(name);
[DoesNotReturn]
private static T ThrowForMissingMember<T>(string name)
=> ThrowHelper.ThrowInvalidOperationException<T>($"Sequence contains no member with name: {name}");
[DoesNotReturn]
private static T ThrowForMissingParameter<T>(string name)
=> ThrowHelper.ThrowInvalidOperationException<T>(
$"Sequence contains no parameter with name: {name}");
private static T? TryGetNamed<T>(this IList<T> memberInfos, string name, Func<T, string> nameGetter)
where T : class
{
for (var i = 0; i < memberInfos.Count; i++)
{
if (nameGetter(memberInfos[i]) == name)
return memberInfos[i];
}
return null;
}
public static TypeReference[] GetTypeReferences(this Type[] types, ModuleDefinition module)
{
var result = new TypeReference[types.Length];
for (var i = types.Length; i-- > 0;)
result[i] = module.ImportReference(types[i]);
return result;
}
public static bool IsGeneric(this TypeReference type)
=> type.ContainsGenericParameter || type.HasGenericParameters || type.IsGenericInstance;
public static bool IsGeneric(this Type type)
=> type.ContainsGenericParameters || type.IsGenericType || type.IsConstructedGenericType;
}
+440 -29
View File
@@ -7,15 +7,20 @@ using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using FisheryLib.Collections;
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
public static class CollectionExtensions
{
public static bool TryGetItem<T>(this List<T> list, int index, [NotNullWhen(true)] out T? item)
{
if (list.Count > index)
if (list._size > index)
{
item = list[index]!;
item = list._items[index]!;
return true;
}
@@ -24,19 +29,23 @@ public static class CollectionExtensions
}
/// <summary>
/// Taken from Span<>.this[] and slower than List<>.this[] due to the ArrayAdjustment
/// Taken from Span.this[] and slower than List.this[] due to the ArrayAdjustment
/// </summary>
internal static T GetItemUnchecked<T>(this List<T> list, int index)
=> Unsafe.Add(ref Unsafe.AddByteOffset(ref Unsafe.As<Pinnable<T>>(list._items).Data, PerTypeValues<T>.ArrayAdjustment), index)!;
=> Unsafe.Add(
ref Unsafe.AddByteOffset(ref Unsafe.As<Pinnable<T>>(list._items).Data, PerTypeValues<T>.ArrayAdjustment),
index)!;
[StructLayout(LayoutKind.Sequential)]
private sealed class Pinnable<T>
{
public T? Data;
}
private static class PerTypeValues<T>
{
public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment();
private static IntPtr MeasureArrayAdjustment()
{
var array = new T[1];
@@ -60,8 +69,8 @@ public static class CollectionExtensions
{
var array = new T[length];
if (enumerable is IList<T> ilist)
array.Fill(ilist);
if (enumerable is IList<T> iList)
array.Fill(iList);
else
array.FillUsingEnumerable(enumerable);
@@ -81,15 +90,16 @@ public static class CollectionExtensions
{
Guard.IsNotNull(destination);
if (enumerable is IList<T> ilist)
destination.Fill(ilist);
if (enumerable is IList<T> iList)
destination.Fill(iList);
else
destination.FillUsingEnumerable(enumerable);
return destination;
}
private static void FillUsingEnumerable<T>(this T[] array, IEnumerable<T> enumerable, int startIndex = 0, int count = -1)
private static void FillUsingEnumerable<T>(this T[] array, IEnumerable<T> enumerable, int startIndex = 0,
int count = -1)
{
var i = 0u;
foreach (var item in enumerable)
@@ -102,7 +112,8 @@ public static class CollectionExtensions
}
}
public static void Fill<Telement, Tcollection>(this Telement[] array, Tcollection collection, int startIndex = 0, int count = -1)
public static void Fill<Telement, Tcollection>(this Telement[] array, Tcollection collection, int startIndex = 0,
int count = -1)
where Tcollection : IList<Telement>
{
Guard.IsNotNull(collection);
@@ -124,8 +135,8 @@ public static class CollectionExtensions
{
Guard.IsNotNull(enumerable);
if (enumerable is IList<T> ilist)
array.Fill(ilist, startIndex, count);
if (enumerable is IList<T> iList)
array.Fill(iList, startIndex, count);
else
array.FillUsingEnumerable(enumerable, startIndex, count);
}
@@ -146,6 +157,27 @@ public static class CollectionExtensions
bucket = entry.next;
}
}
return ref ThrowKeyNotFoundException<TKey, TValue>(key);
}
public static ref TValue GetReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
{
if (dictionary.buckets != null)
{
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
{
ref var entry = ref dictionary.entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
}
}
return ref ThrowKeyNotFoundException<TKey, TValue>(key);
}
@@ -153,6 +185,7 @@ public static class CollectionExtensions
/// Returns a reference to the value field of an entry if the key exists and an Unsafe.NullRef{TValue} if not.
/// Must be checked with Unsafe.IsNullRef before assigning to a regular var or field without ref.
/// </summary>
/// <param name="dictionary">The instance</param>
/// <param name="key">The key</param>
/// <returns>ref TValue or Unsafe.NullRef{TValue}</returns>
public static ref TValue TryGetReferenceUnsafe<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
@@ -171,11 +204,11 @@ public static class CollectionExtensions
bucket = entry.next;
}
}
return ref Unsafe.NullRef<TValue>();
}
public static ref TValue TryGetReferenceUnsafe<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
where TKey : struct
{
if (dictionary.buckets != null)
{
@@ -185,12 +218,13 @@ public static class CollectionExtensions
while (bucket >= 0)
{
ref var entry = ref dictionary.entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals(ref key))
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
}
}
return ref Unsafe.NullRef<TValue>();
}
@@ -210,6 +244,7 @@ public static class CollectionExtensions
bucket = entry.next;
}
}
return default;
}
@@ -230,9 +265,162 @@ public static class CollectionExtensions
bucket = entry.next;
}
}
return default;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
where TValue : new()
{
var keyCode = HashCode.Get(key) & 0x7FFFFFFF;
StartOfLookup:
if (dictionary.buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
{
ref var entry = ref dictionary.entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return ref entry.value;
bucket = entry.next;
}
}
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TValue GetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
where TValue : new()
{
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
StartOfLookup:
if (dictionary.buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
{
ref var entry = ref dictionary.entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return ref entry.value;
bucket = entry.next;
}
}
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
// public static bool TryGetOrAddReference<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
// ref TValue reference)
// {
// var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
// var result = true;
//
// StartOfLookup:
// if (dictionary.buckets != null)
// {
// var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
// while (bucket >= 0)
// {
// ref var entry = ref dictionary.entries[bucket];
// if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
// {
// reference.Value = ref entry.value;
// return result;
// }
//
// bucket = entry.next;
// }
// }
//
// result = false;
// dictionary.Add(key, Reflection.New<TValue>());
// goto StartOfLookup;
// }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
where TValue : new()
{
var keyCode = HashCode.Get(key) & 0x7FFFFFFF;
StartOfLookup:
if (dictionary.buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
{
ref var entry = ref dictionary.entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
return entry.value;
bucket = entry.next;
}
}
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, ref TKey key)
where TValue : new()
{
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
StartOfLookup:
if (dictionary.buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
{
ref var entry = ref dictionary.entries[bucket];
if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key))
return entry.value;
bucket = entry.next;
}
}
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
public static bool TryGetOrAddValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key,
out TValue value)
{
var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF;
var result = true;
StartOfLookup:
if (dictionary.buckets != null)
{
var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length];
while (bucket >= 0)
{
ref var entry = ref dictionary.entries[bucket];
if (entry.hashCode == keyCode && entry.key.Equals<TKey>(key))
{
value = entry.value;
return result;
}
bucket = entry.next;
}
}
result = false;
dictionary.Add(key, Reflection.New<TValue>());
goto StartOfLookup;
}
[DoesNotReturn]
private static ref V ThrowKeyNotFoundException<T, V>(T key)
{
@@ -243,42 +431,106 @@ public static class CollectionExtensions
[DoesNotReturn]
private static void ThrowKeyNotFoundException(object key)
=> throw new KeyNotFoundException(
string.Format("The given key '{0}' was not present in the dictionary.", key));
=> throw new KeyNotFoundException($"The given key '{key}' was not present in the dictionary.");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref int Version<T>(this List<T> list) => ref list._version;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddRangeFast<T>(this List<T> list, List<T> range)
{
if (range._size < 1)
return;
list.EnsureCapacity(list._size + range._size);
range.UnsafeCopyTo(list, list._size);
list._size += range._size;
list._version++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsafeCopyTo<T>(this List<T> source, List<T> destination, int destinationStartIndex)
=> UnsafeBlockCopy(ref source._items[0], ref destination._items[destinationStartIndex], source._size);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void UnsafeCopyTo<T>(this T[] source, T[] destination, int destinationStartIndex)
=> UnsafeBlockCopy(ref source[0], ref destination[destinationStartIndex], source.Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void UnsafeBlockCopy<T>(ref T source, ref T destination, int count)
=> Unsafe.CopyBlock(ref Unsafe.As<T, byte>(ref destination),
ref Unsafe.As<T, byte>(ref source),
(uint)((nint)sizeof(T) * count));
/// <summary>
/// Returns a list's internal _items array. Keep in mind list.Version() needs to change every time items are modified.
/// Returns a list's internal _items array. Keep in mind list.Version() needs to change every time items are
/// modified.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T[] ItemsUnchecked<T>(this List<T> list) => list._items;
/// <summary>
/// Returns a span pointing to a list's internal _items array. Keep in mind list.Version() needs to change every time items are modified.
/// Returns a span pointing to a list's internal _items array. Keep in mind list.Version() needs to change every
/// time items are modified.
/// </summary>
public static Span<T> AsSpanUnchecked<T>(this List<T> list) => new(list._items, 0, list.Count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> AsSpanUnchecked<T>(this List<T> list) => new(list._items, 0, list._size);
public static ReadOnlySpan<T> AsReadOnlySpan<T>(this List<T> list) => new(list._items, 0, list.Count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<T> AsReadOnlySpan<T>(this List<T> list) => new(list._items, 0, list._size);
public static void ReplaceContentsWith<T, V>(this List<T> list, V collection) where V : ICollection<T>
public static void ReplaceContentsWith<T>(this List<T> list, [AllowNull] List<T> collection)
{
if (ReferenceEquals(list, collection))
return;
if (collection is null
|| (collection.Count is var collectionCount
&& collectionCount == 0))
|| (collection._size is var collectionCount
&& collectionCount == 0))
{
list.Clear();
return;
}
if (collectionCount <= list.Count)
if (collectionCount <= list._size)
{
if (collectionCount < list.Count)
list.RemoveRange(collectionCount, list.Count - collectionCount);
if (collectionCount < list._size)
list.RemoveRange(collectionCount, list._size - collectionCount);
collection.UnsafeCopyTo(list, 0);
}
else
{
list._items = new T[collectionCount];
collection.UnsafeCopyTo(list, 0);
list._size = collectionCount;
}
list._version++;
}
public static void ReplaceContentsWith<T, V>(this List<T> list, [AllowNull] V collection) where V : ICollection<T>
{
if (typeof(V) == typeof(List<T>))
list.ReplaceContentsWith(Unsafe.As<List<T>>(collection));
if (ReferenceEquals(list, collection))
return;
if (collection is null
|| (collection.Count is var collectionCount
&& collectionCount == 0))
{
list.Clear();
return;
}
if (collectionCount <= list._size)
{
if (collectionCount < list._size)
list.RemoveRange(collectionCount, list._size - collectionCount);
collection.CopyTo(list._items, 0);
}
else
@@ -287,18 +539,176 @@ public static class CollectionExtensions
collection.CopyTo(list._items, 0);
list._size = collectionCount;
}
list._version++;
}
public static FishTable<TKey, TSource> ToFishTable<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
=> source.ToFishTable(keySelector, static item => item);
public static FishTable<TKey, TValue> ToFishTable<TSource, TKey, TValue>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, Func<TSource, TValue> valueSelector)
{
Guard.IsNotNull(source);
Guard.IsNotNull(keySelector);
Guard.IsNotNull(valueSelector);
var capacity = 0;
if (source is ICollection iCollection)
capacity = (int)(iCollection.Count * 1.5f);
var fishTable = new FishTable<TKey, TValue>(capacity);
fishTable.AddRange(source, keySelector, valueSelector);
return fishTable;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Clear<T>(this T[] array) => Array.Clear(array, 0, array.Length);
// public static List<T> Copy<T>(this List<T> source)
// {
// Guard.IsNotNull(source);
//
// var destination = new List<T>(source._size);
//
// if (source._size == 0)
// return destination;
//
// // Unsafe.CopyBlock(ref Unsafe.As<T, byte>(ref destination._items[0]),
// // ref Unsafe.As<T, byte>(ref source._items[0]), (uint)sizeof(T) * (uint)source._size);
//
// FisheryLib.CollectionExtensions.UnsafeBlockCopy(ref source._items[0], // does a type check that causes issues
// ref destination._items[0], source._size); // when called from transpiled methods
//
// destination._size = source._size;
//
// return destination;
// }
public static unsafe List<T> Copy<T>(this List<T> source)
{
Guard.IsNotNull(source);
var destination = new List<T>(source._size);
if (source._size == 0)
return destination;
fixed (void* destinationPointer = &destination._items[0], sourcePointer = &source._items[0])
Unsafe.CopyBlock(destinationPointer, sourcePointer, (uint)(source._size * sizeof(T)));
destination._size = source._size;
return destination;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddRangeFast<T>(this List<T> list, T[] range)
{
Guard.IsNotNull(list);
Guard.IsNotNull(range);
if (range.Length < 1)
return;
list.EnsureCapacity(list._size + range.Length);
range.UnsafeCopyTo(list._items, list._size);
list._size += range.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(this List<char> list, string value)
{
Guard.IsNotNull(list);
Guard.IsNotNull(value);
if (value.Length < 1)
return;
list.EnsureCapacity(list._size + value.Length);
UnsafeBlockCopy(ref value.m_firstChar,
ref list._items[list._size], value.Length);
list._size += value.Length;
list._version++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static StringBuilder Append(this StringBuilder builder, List<char> value)
=> builder.Append(value._items, 0, value._size);
public static ref T GetReferenceUnverifiable<T>(this List<T> list, int index) => ref list._items[index];
public static IList TryNew(IEnumerable enumerable, Type type) => GetListExtensionMethods(type).New(enumerable);
public static void TryAddRange<T>(this IList list, T collection, Type type) where T : ICollection => GetListExtensionMethods(type).AddRange(list, collection);
public static void TryAddRange<T>(this IList list, T collection, Type type) where T : ICollection
=> GetListExtensionMethods(type).AddRange(list, collection);
public static Array TryToArray(this IList list, Type type) => GetListExtensionMethods(type).ToArray(list);
public static unsafe uint GetSizeEstimate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary)
=> dictionary.GetType().ComputeManagedObjectSizeEstimate()
+ ((uint)sizeof(Dictionary<TKey, TValue>.Entry) * (uint)(dictionary.entries?.Length ?? 0))
+ (sizeof(int) * (uint)(dictionary.buckets?.Length ?? 0));
internal static unsafe uint ComputeManagedObjectSizeEstimate(this Type type)
{
var objectHeader = (uint)sizeof(void*) * 2u;
ref var cachedSize = ref CachedManagedObjectSizes.GetOrAddReference(type.TypeHandle.Value);
if (cachedSize.Size == uint.MaxValue)
UpdateCachedManagedObjectSize(type, ref cachedSize);
return objectHeader + cachedSize.Size;
}
private static void UpdateCachedManagedObjectSize(Type type, ref ManagedObjectSize cachedSize)
{
cachedSize.Size = 0;
var fields = type.GetFields(BindingFlags.Instance
| BindingFlags.Public
| BindingFlags.NonPublic);
for (var i = fields.Length; i-- > 0;)
{
var fieldType = fields[i].FieldType;
if (!typeof(ValueType).IsAssignableFrom(fieldType) || fieldType == typeof(ValueType))
// IsValueType and IsSubclassOf somehow cause StackOverflows here
cachedSize.Size += (uint)IntPtr.Size;
else if (fieldType.IsPrimitive)
cachedSize.Size += (uint)Marshal.SizeOf(fieldType);
else if (fieldType != type)
cachedSize.Size += fieldType.ComputeManagedObjectSizeEstimate();
else
cachedSize.Size += (uint)IntPtr.Size; // fuck it
}
var remainder = cachedSize.Size % (uint)IntPtr.Size;
if (remainder != 0u)
cachedSize.Size += (uint)IntPtr.Size - remainder;
}
private static FishTable<IntPtr, ManagedObjectSize> CachedManagedObjectSizes => _cachedManagedObjectSizes ??= new();
[ThreadStatic]
private static FishTable<IntPtr, ManagedObjectSize>? _cachedManagedObjectSizes;
private struct ManagedObjectSize
{
public uint Size = uint.MaxValue;
[UsedImplicitly]
public ManagedObjectSize()
{
}
}
public static GenericBase GetListExtensionMethods(Type type)
=> ListExtensionCache.TryGetValue(type)
?? CreateListExtensionMethods(type);
?? CreateListExtensionMethods(type);
private static GenericBase CreateListExtensionMethods(Type type)
{
@@ -322,7 +732,8 @@ public static class CollectionExtensions
public class Generic<T> : GenericBase
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void AddRange(IList list, ICollection collection) => ((List<T>)list).AddRange((IEnumerable<T>)collection);
public override void AddRange(IList list, ICollection collection)
=> ((List<T>)list).AddRange((IEnumerable<T>)collection);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override Array ToArray(IList list) => ((List<T>)list).ToArray();
+862
View File
@@ -0,0 +1,862 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//
// Loosely based on https://github.com/matthewcrews/FastDictionaryTest/tree/main
// which itself is based on
// https://probablydance.com/2018/05/28/a-new-fast-hash-table-in-response-to-googles-new-fast-hash-table/
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using FisheryLib.FunctionPointers;
using FisheryLib.Utility;
using JetBrains.Annotations;
namespace FisheryLib.Collections;
#pragma warning disable CS8766, CS8767
[PublicAPI]
[StructLayout(LayoutKind.Sequential)]
public class FishSet<T> : ISet<T>, IReadOnlyCollection<T>, ICollection
{
private const int FIBONACCI_HASH = -1640531527; // unchecked((int)(uint)Math.Round(uint.MaxValue / FishMath.PHI));
internal T?[] _buckets;
private unsafe delegate*<T?, int> _hashCodeGetter = GetHashCode<T?>.Default;
private unsafe delegate*<T?, T?, bool> _equalityComparer = Equals<T?>.Default;
private int
_bucketBitShift,
_count;
internal Tails _tails;
private int
_wrapAroundMask,
_version;
private float _maxLoadFactor = 0.5f;
public int Version
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _version;
set
{
Guard.IsGreaterThan(value, _version);
_version = value;
}
}
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _count;
}
public object SyncRoot => throw new NotImplementedException();
public bool IsSynchronized => throw new NotImplementedException();
public bool IsReadOnly => false;
public event Action<T>?
EntryAdded,
EntryRemoved;
public float MaxLoadFactor
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _maxLoadFactor;
set
{
Guard.IsInRange(value, 0f, 1f);
_maxLoadFactor = value;
}
}
private static T AssertKeyType(object key)
{
if (key is T tKey)
return tKey;
else if (key != null!)
ThrowHelper.ThrowWrongKeyTypeArgumentException(key);
return default!;
}
public bool this[T key]
{
[CollectionAccess(CollectionAccessType.Read)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Contains(key);
[CollectionAccess(CollectionAccessType.UpdatedContent)]
set => InsertEntry(key, value);
}
public FishSet() : this(0)
{
}
public FishSet(int minimumCapacity) => Initialize(minimumCapacity);
[MemberNotNull(nameof(_buckets))]
[MemberNotNull(nameof(_tails))]
private void Initialize(int minimumCapacity = 0)
{
minimumCapacity = minimumCapacity <= 4 ? 4 : FishMath.NextPowerOfTwo(minimumCapacity);
// Mathf.NextPowerOfTwo(minimumCapacity);
_buckets = new T?[minimumCapacity];
_tails = new((uint)minimumCapacity);
_bucketBitShift = 32 - FishMath.TrailingZeroCount((uint)_buckets.Length);
_wrapAroundMask = _buckets.Length - 1;
}
public FishSet(IEnumerable<T> entries) : this(0)
{
foreach (var entry in entries)
InsertEntry(entry, false);
}
private int GetTailIndex(int entryIndex) => GetIndexForTail(entryIndex, _tails[(uint)entryIndex]);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetIndexForTail(int entryIndex, uint tail) => (entryIndex + GetJumpDistance(tail)) & _wrapAroundMask;
#region Junk
// private static long GetJumpDistanceLong(byte index)
// => index switch
// {
// 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7,
// 8 => 8, 9 => 9, 10 => 10, 11 => 11, 12 => 12, 13 => 13, 14 => 14,
// 15 => 15, 16 => 21, 17 => 28, 18 => 36, 19 => 45, 20 => 55, 21 => 66,
// 22 => 78, 23 => 91, 24 => 105, 25 => 120, 26 => 136, 27 => 153,
// 28 => 171, 29 => 190, 30 => 210, 31 => 231, 32 => 253, 33 => 276,
// 34 => 300, 35 => 325, 36 => 351, 37 => 378, 38 => 406, 39 => 435,
// 40 => 465, 41 => 496, 42 => 528, 43 => 561, 44 => 595, 45 => 630,
// 46 => 666, 47 => 703, 48 => 741, 49 => 780, 50 => 820, 51 => 861,
// 52 => 903, 53 => 946, 54 => 990, 55 => 1035, 56 => 1081, 57 => 1128,
// 58 => 1176, 59 => 1225, 60 => 1275, 61 => 1326, 62 => 1378, 63 => 1431,
// 64 => 1485, 65 => 1540, 66 => 1596, 67 => 1653, 68 => 1711, 69 => 1770,
// 70 => 1830, 71 => 1891, 72 => 1953, 73 => 2016, 74 => 2080, 75 => 2145,
// 76 => 2211, 77 => 2278, 78 => 2346, 79 => 2415, 80 => 2485, 81 => 2556,
// 82 => 3741, 83 => 8385, 84 => 18915, 85 => 42486, 86 => 95703, 87 => 215496,
// 88 => 485605, 89 => 1091503, 90 => 2456436, 91 => 5529475, 92 => 12437578,
// 93 => 27986421, 94 => 62972253, 95 => 141700195, 96 => 318819126, 97 => 717314626,
// 98 => 1614000520, 99 => 3631437253, 100 => 8170829695, 101 => 18384318876, 102 => 41364501751,
// 103 => 93070021080, 104 => 209407709220, 105 => 471167588430, 106 => 1060127437995, 107 => 2385287281530,
// 108 => 5366895564381, 109 => 12075513791265, 110 => 27169907873235, 111 => 61132301007778,
// 112 => 137547673121001, 113 => 309482258302503, 114 => 696335090510256, 115 => 1566753939653640,
// 116 => 3525196427195653, 117 => 7931691866727775, 118 => 17846306747368716,
// 119 => 40154190394120111, 120 => 90346928493040500, 121 => 203280588949935750,
// 122 => 457381324898247375, 123 => 1029107980662394500, 124 => 2315492957028380766,
// 125 => 5209859150892887590, > 125 => throw new()
// };
// private static int GetJumpDistance(byte index)
// => index switch
// {
// 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 10 => 10, 11 => 11,
// 12 => 12, 13 => 13, 14 => 14, 15 => 15, 16 => 21, 17 => 28, 18 => 36, 19 => 45, 20 => 55, 21 => 66,
// 22 => 78, 23 => 91, 24 => 105, 25 => 120, 26 => 136, 27 => 153, 28 => 171, 29 => 190, 30 => 210, 31 => 231,
// 32 => 253, 33 => 276, 34 => 300, 35 => 325, 36 => 351, 37 => 378, 38 => 406, 39 => 435, 40 => 465,
// 41 => 496, 42 => 528, 43 => 561, 44 => 595, 45 => 630, 46 => 666, 47 => 703, 48 => 741, 49 => 780,
// 50 => 820, 51 => 861, 52 => 903, 53 => 946, 54 => 990, 55 => 1035, 56 => 1081, 57 => 1128, 58 => 1176,
// 59 => 1225, 60 => 1275, 61 => 1326, 62 => 1378, 63 => 1431, 64 => 1485, 65 => 1540, 66 => 1596, 67 => 1653,
// 68 => 1711, 69 => 1770, 70 => 1830, 71 => 1891, 72 => 1953, 73 => 2016, 74 => 2080, 75 => 2145, 76 => 2211,
// 77 => 2278, 78 => 2346, 79 => 2415, 80 => 2485, 81 => 2556, 82 => 3741, 83 => 8385, 84 => 18915,
// 85 => 42486, 86 => 95703, 87 => 215496, 88 => 485605, 89 => 1091503, 90 => 2456436, 91 => 5529475,
// 92 => 12437578, 93 => 27986421, 94 => 62972253, 95 => 141700195, 96 => 318819126, 97 => 717314626,
// 98 => 1614000520, 255 => int.MaxValue, > 98 => ThrowForInvalidIndex(index)
// };
#endregion
private static int GetJumpDistance(uint index)
=> index switch
{
2 => 1, 3 => 2, 4 => 3, 5 => 4, 6 => 5, 7 => 8, 8 => 13, 9 => 21, 10 => 34, 11 => 55, 12 => 89, 13 => 144,
14 => 233, 15 => 377, _ => MultiplyWithPhi(index)
};
[MethodImpl(MethodImplOptions.NoInlining)]
[DoesNotReturn]
private static int MultiplyWithPhi(uint index)
=> throw new NotImplementedException(); // (int)(uint)Math.Round(index * FishMath.PHI);
// private static int ThrowForInvalidIndex(byte index)
// => throw new($"Jump distance index for FishTable too large. Should be < 99, got {index} instead.");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsTail(int bucketIndex)
=> bucketIndex != GetBucketIndexForKeyAt(bucketIndex);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsTail(T entry, int bucketIndex)
=> bucketIndex != GetBucketIndex(entry);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool HasTail(int entryIndex) => !_tails.IsSoloOrEmpty(entryIndex);
private void SetEntryWithoutTail(int bucketIndex, T entry)
{
_buckets[bucketIndex] = entry;
_tails.SetSolo(bucketIndex);
}
private void SetEntryAsTail(int bucketIndex, T entry, int parentIndex, uint offset)
{
SetEntryWithoutTail(bucketIndex, entry);
_tails[(uint)parentIndex] = offset;
}
private void SetParentTail(int entryIndex, uint newTail)
=> _tails[(uint)GetParentBucketIndex(entryIndex)] = newTail;
private bool InsertEntry(T entry, bool allowReplace = true, bool shifting = false)
{
var addedNewEntry = InsertEntryInternal(entry, allowReplace);
if (shifting)
return addedNewEntry;
_version++;
if (!addedNewEntry)
return false;
OnEntryAdded(entry);
return true;
}
private void OnEntryAdded(T entry)
{
// _version++; handled separately
_count++;
EntryAdded?.Invoke(entry);
}
/// <summary>
/// Returns true when adding a new entry, false for replacing an existing entry. AllowReplace: false causes throwing
/// instead. Does not adjust Count, Version or invoke EntryAdded.
/// </summary>
private bool InsertEntryInternal(T entry, bool allowReplace = true)
{
StartOfMethod:
var bucketIndex = GetBucketIndex(entry);
ref var bucket = ref _buckets[bucketIndex];
if (IsBucketEmpty(bucketIndex))
{
SetEntryWithoutTail(bucketIndex, entry);
return true;
}
if (TryReplaceValueOfMatchingKey(entry, allowReplace, ref bucket!))
return false;
if (CheckResize())
goto StartOfMethod;
if (IsTail(bucket, bucketIndex))
{
var previousEntry = bucket;
var hasTail = !_tails.IsSolo(bucketIndex);
var tailingEntries = hasTail ? new T[GetTailCount(bucketIndex)] : null;
if (hasTail)
{
var i = 0;
var tailIndex = GetTailIndex(bucketIndex);
while (true)
{
ref var tailingBucket = ref _buckets[tailIndex];
tailingEntries![i++] = tailingBucket!;
tailingBucket = default;
if (!_tails.IsSoloOrEmpty(tailIndex))
{
var nextTailIndex = GetTailIndex(tailIndex);
_tails.SetEmpty(tailIndex);
tailIndex = nextTailIndex;
}
else
{
_tails.SetEmpty(tailIndex);
break;
}
}
}
SetParentTail(bucketIndex, Tails.SOLO);
SetEntryWithoutTail(bucketIndex, entry);
InsertEntry(previousEntry, shifting: true);
if (hasTail)
{
for (var i = 0; i < tailingEntries!.Length; i++)
InsertEntry(tailingEntries[i], shifting: true);
}
return true;
}
else
{
return !TryFindTailIndexAndReplace(entry, ref bucketIndex, allowReplace)
&& InsertAsTail(entry, bucketIndex);
}
}
private int GetTailCount(int index)
{
var tailCount = 0;
while (HasTail(index))
{
tailCount++;
index = GetTailIndex(index);
}
return tailCount;
}
private bool IsBucketEmpty(int bucketIndex) => IsBucketEmpty(bucketIndex, _tails, _buckets);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool IsBucketEmpty(int bucketIndex, Tails tails, T?[] buckets) => tails.IsEmpty(bucketIndex);
private bool TryFindTailIndexAndReplace(T entry, ref int bucketIndex, bool allowReplace)
{
while (true)
{
if (!TryContinueWithTail(ref bucketIndex))
return false;
if (TryReplaceValueOfMatchingKey(entry, allowReplace, ref _buckets[bucketIndex]!))
return true;
}
}
private unsafe bool TryReplaceValueOfMatchingKey(T entry, bool allowReplace, ref T bucket)
{
if (!_equalityComparer(bucket, entry))
return false;
if (!allowReplace)
ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(entry);
bucket = entry;
return true;
}
private bool InsertAsTail(T entry, int bucketIndex)
{
var parentIndex = bucketIndex;
var offset = 2u;
while (true)
{
bucketIndex = GetIndexForTail(parentIndex, offset);
if (IsBucketEmpty(bucketIndex))
{
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
return true;
}
if (IsTail(bucketIndex) && offset < _tails[(uint)GetParentBucketIndex(bucketIndex)])
break;
if (++offset <= 15u)
continue;
if (_buckets.Length > Count * 5)
WarnForPossiblyExcessiveResizing(entry);
Resize();
InsertEntry(entry, false, true);
return true;
}
var previousEntry = _buckets[bucketIndex];
var hasTail = !_tails.IsSolo(bucketIndex);
var tailingEntries = hasTail ? new T?[GetTailCount(bucketIndex)] : null;
if (hasTail)
{
var k = 0;
var tailIndex = GetTailIndex(bucketIndex);
while (true)
{
ref var tailingBucket = ref _buckets[tailIndex];
tailingEntries![k] = tailingBucket;
k++;
tailingBucket = default;
if (!_tails.IsSoloOrEmpty(tailIndex))
{
var nextTailIndex = GetTailIndex(tailIndex);
_tails.SetEmpty(tailIndex);
tailIndex = nextTailIndex;
}
else
{
_tails.SetEmpty(tailIndex);
break;
}
}
}
SetParentTail(bucketIndex, Tails.SOLO);
SetEntryAsTail(bucketIndex, entry, parentIndex, offset);
InsertEntry(previousEntry!, shifting: true);
if (hasTail)
{
for (var k = 0; k < tailingEntries!.Length; k++)
InsertEntry(tailingEntries[k]!, shifting: true);
}
return true;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void WarnForPossiblyExcessiveResizing(T entry)
=> Log.Warning($"FishTable is resizing from a large number of clashing hashCodes. Last inserted key: '{
entry}', count: '{_count}', bucket array length: '{
_buckets.Length}', tailing entries: '{GetTailingEntriesCount()}'");
private int GetTailingEntriesCount()
{
var count = 0;
for (var i = _buckets.Length; i-- > 0;)
{
if (!IsBucketEmpty(i) && IsTail(i))
count++;
}
return count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CheckResize()
{
if (_count <= (int)(_buckets.Length * _maxLoadFactor))
return false;
Resize();
return true;
}
private void Resize()
{
var oldBuckets = _buckets;
var oldTails = _tails;
_tails = new((uint)_buckets.Length << 1);
_buckets = new T?[_buckets.Length << 1];
_bucketBitShift = 32 - FishMath.TrailingZeroCount((uint)_buckets.Length);
_wrapAroundMask = _buckets.Length - 1;
for (var i = 0; i < oldBuckets.Length; i++)
{
if (!IsBucketEmpty(i, oldTails, oldBuckets))
InsertEntry(oldBuckets[i]!, false, true);
}
}
[CollectionAccess(CollectionAccessType.Read)]
public unsafe bool Contains(T key)
{
var bucketIndex = GetBucketIndex(key);
while (true)
{
if (_equalityComparer(key, _buckets[bucketIndex]))
return true;
if (!TryContinueWithTail(ref bucketIndex))
return false;
}
}
#region GetBucketIndexMethods
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe int GetBucketIndex(T key) => GetBucketIndex(_hashCodeGetter(key));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetBucketIndex(int hashCode) => (hashCode * FIBONACCI_HASH) >>> _bucketBitShift;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetBucketIndexForKeyAt(int index) => GetBucketIndex(_buckets[index]!);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetParentBucketIndex(int childBucketIndex)
{
var ancestorIndex = GetBucketIndexForKeyAt(childBucketIndex);
var i = 0;
while (true)
{
if (i++ > 32)
ThrowHelper.ThrowFailedToFindParentInvalidOperationException(this, childBucketIndex);
var nextIndex = GetTailIndex(ancestorIndex);
if (nextIndex == childBucketIndex)
break;
ancestorIndex = nextIndex;
}
return ancestorIndex;
}
#endregion
/// <summary>
/// Returns false for solo or empty, otherwise sets the index to the tail index and returns true
/// </summary>
[MethodImpl(MethodImplOptions.NoInlining)]
private bool TryContinueWithTail(ref int entryIndex)
{
if (!HasTail(entryIndex))
return false;
entryIndex = GetTailIndex(entryIndex);
return true;
}
[CollectionAccess(CollectionAccessType.UpdatedContent)]
public bool Add(T key)
{
Guard.IsNotNull(key);
return InsertEntry(key, false);
}
public void UnionWith(IEnumerable<T> other) => throw new NotImplementedException();
public void IntersectWith(IEnumerable<T> other) => throw new NotImplementedException();
public void ExceptWith(IEnumerable<T> other) => throw new NotImplementedException();
public void SymmetricExceptWith(IEnumerable<T> other) => throw new NotImplementedException();
public bool IsSubsetOf(IEnumerable<T> other) => throw new NotImplementedException();
public bool IsSupersetOf(IEnumerable<T> other) => throw new NotImplementedException();
public bool IsProperSupersetOf(IEnumerable<T> other) => throw new NotImplementedException();
public bool IsProperSubsetOf(IEnumerable<T> other) => throw new NotImplementedException();
public bool Overlaps(IEnumerable<T> other) => throw new NotImplementedException();
public bool SetEquals(IEnumerable<T> other) => throw new NotImplementedException();
[CollectionAccess(CollectionAccessType.ModifyExistingContent)]
public bool Remove(T key)
{
if (!RemoveInternal(key))
return false;
OnEntryRemoved(key);
return true;
}
private void EmplaceWithTails(int entryIndex)
{
ref var bucket = ref _buckets[entryIndex];
var entry = bucket;
Span<int> tailIndices = stackalloc int[GetTailCount(entryIndex)];
if (HasTail(entryIndex))
{
tailIndices[0] = GetTailIndex(entryIndex);
for (var i = 1; i < tailIndices.Length; i++)
tailIndices[i] = GetTailIndex(tailIndices[i - 1]);
}
if (IsTail(entry!, entryIndex))
SetParentTail(entryIndex, Tails.SOLO);
bucket = default;
_tails.SetEmpty(entryIndex);
InsertEntry(entry!, shifting: true);
for (var i = 1; i < tailIndices.Length; i++)
{
var tailIndex = tailIndices[i];
ref var tailBucket = ref _buckets[tailIndex];
var tailEntry = tailBucket;
tailBucket = default;
_tails.SetEmpty(tailIndex);
InsertEntry(tailEntry!, shifting: true);
}
}
private unsafe bool RemoveInternal(T key)
{
var bucketIndex = GetBucketIndex(key);
while (true)
{
ref var entry = ref _buckets[bucketIndex];
if (_equalityComparer(key, entry))
{
var tailIndex = -1;
if (IsTail(entry!, bucketIndex))
{
if (HasTail(bucketIndex))
tailIndex = GetTailIndex(bucketIndex);
else
SetParentTail(bucketIndex, Tails.SOLO);
}
entry = default;
_tails.SetEmpty(bucketIndex);
if (tailIndex != -1)
EmplaceWithTails(tailIndex);
return true;
}
if (TryContinueWithTail(ref bucketIndex))
continue;
return false;
}
}
void ICollection<T>.Add(T item) => Add(item);
[CollectionAccess(CollectionAccessType.ModifyExistingContent)]
public void Clear()
{
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (_buckets is null)
Initialize();
Array.Clear(_buckets, 0, _buckets.Length);
_tails.Reset();
_count = 0;
_version++;
}
[CollectionAccess(CollectionAccessType.Read)]
public void CopyTo(T[] array, int arrayIndex)
{
var length = _buckets.Length;
for (var i = 0; i < length; i++)
{
if (IsBucketEmpty(i))
continue;
array[arrayIndex] = _buckets[i]!;
arrayIndex++;
}
}
private void OnEntryRemoved(T entry)
{
_version++;
_count--;
EntryRemoved?.Invoke(entry);
}
#region InterfaceMethods
[CollectionAccess(CollectionAccessType.Read)]
public Enumerator GetEnumerator() => new(this, Enumerator.KEY_VALUE_PAIR);
IEnumerator<T> IEnumerable<T>.GetEnumerator()
=> new Enumerator(this, Enumerator.KEY_VALUE_PAIR);
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this, Enumerator.KEY_VALUE_PAIR);
void ICollection.CopyTo(Array array, int index)
{
if (array is T[] typedArray)
{
CopyTo(typedArray, index);
return;
}
var length = _buckets.Length;
for (var i = 0; i < length; i++)
{
if (IsBucketEmpty(i))
continue;
((IList)array)[index] = _buckets[i];
index++;
}
}
#endregion
#region Structs
internal struct Tails
{
public const uint
EMPTY = 0u,
SOLO = 1u;
private NibbleArray _values;
public Tails(uint length) => _values = new(length);
public uint this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _values[index];
set => _values[index] = value;
}
public void Reset() => _values.Clear(); //_values.Initialize(15u);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsEmpty(int index) => _values[(uint)index] == EMPTY;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSolo(int index) => _values[(uint)index] == SOLO;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsSoloOrEmpty(int index) => _values[(uint)index] <= 1u;
public void SetSolo(int index) => _values[(uint)index] = SOLO;
public void SetEmpty(int index) => _values[(uint)index] = EMPTY;
}
public record struct Enumerator : IEnumerator<T>
{
private readonly FishSet<T> _dictionary;
private readonly int _version;
private uint _index;
private T? _current;
internal const int DICT_ENTRY = 1;
internal const int KEY_VALUE_PAIR = 2;
internal Enumerator(FishSet<T> dictionary, int getEnumeratorRetType)
{
_dictionary = dictionary;
_version = dictionary._version;
_index = 0u;
_current = default;
}
public bool MoveNext()
{
if (_version != _dictionary._version)
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
while (_index < (uint)_dictionary._buckets.Length)
{
if (_dictionary.IsBucketEmpty((int)_index++))
continue;
_current = _dictionary._buckets[_index - 1]!;
return true;
}
_index = (uint)_dictionary._buckets.Length + 1u;
_current = default;
return false;
}
[CollectionAccess(CollectionAccessType.Read)]
public T Current => _current!;
public void Dispose()
{
}
object IEnumerator.Current
{
get
{
if (_index == 0u || _index == (uint)_dictionary._buckets.Length + 1u)
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
return _current!;
}
}
void IEnumerator.Reset()
{
if (_version != _dictionary._version)
ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion();
_index = 0u;
_current = default;
}
}
#endregion
private static class ThrowHelper
{
[DoesNotReturn]
internal static void ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion()
=> throw new InvalidOperationException("Collection was modified; enumeration operation may not execute.");
[DoesNotReturn]
internal static void ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen()
=> throw new InvalidOperationException("Enumeration has either not started or has already finished.");
[DoesNotReturn]
internal static void ThrowWrongKeyTypeArgumentException(object? key)
{
Guard.IsNotNull(key);
throw new ArgumentException($"The value \"{key}\" is not of type \"{
typeof(T)}\" and cannot be used in this generic collection.",
nameof(key));
}
[DoesNotReturn]
internal static void ThrowAddingDuplicateWithKeyArgumentException(T key)
{
Guard.IsNotNull(key);
ThrowAddingDuplicateWithKeyArgumentException((object)key);
}
[DoesNotReturn]
internal static unsafe void ThrowFailedToFindParentInvalidOperationException(FishSet<T> fishTable,
int childBucketIndex)
{
var entry = fishTable._buckets[childBucketIndex];
throw new InvalidOperationException($"Failed to find parent index in FishTable for key: '{
entry}', hashCode: '{fishTable._hashCodeGetter(entry)}', count: '{
fishTable._count}', bucket array length: '{fishTable._buckets.Length}', tailing entries: '{
fishTable.GetTailingEntriesCount()}'");
}
[DoesNotReturn]
private static void ThrowAddingDuplicateWithKeyArgumentException(object key)
=> throw new ArgumentException($"An item with the same key has already been added. Key: {key}");
}
}
#pragma warning restore CS8766, CS8767
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,62 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.Collections;
[PublicAPI]
public struct NibbleArray
{
private uint _length;
private byte[] _data;
public uint this[uint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Guard.IsLessThan(index, _length);
return (index & 1u) == 0u ? _data[index >> 1] & 0b0000_1111u : (uint)_data[index >> 1] >> 4;
}
set
{
Guard.IsLessThan(value, 16u);
Guard.IsLessThan(index, _length);
_data[index >> 1] = (byte)((index & 1u) == 0u
? (_data[index >> 1] & 0b1111_0000u) | value
: (_data[index >> 1] & 0b0000_1111u) | (value << 4));
}
}
public uint Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _length;
}
public NibbleArray(uint length)
{
_length = length;
_data = new byte[(length + 1u) >> 1];
}
public void Resize(uint newSize)
{
_length = newSize;
Array.Resize(ref _data, (int)(newSize + 1u) >> 1);
}
public void Clear() => _data.Clear();
public void Initialize(uint value)
{
Guard.IsLessThan(value, 16u);
Unsafe.InitBlockUnaligned(ref _data[0u], (byte)(value | (value << 4)), (uint)_data.Length);
}
}
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -7,23 +7,23 @@ using System.Globalization;
using System.Runtime.CompilerServices;
namespace FisheryLib;
public static class Convert
{
[Obsolete("Renamed to Convert.Type for clarity")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe TTo To<TFrom, TTo>(TFrom from) where TFrom : IConvertible
=> Type<TFrom, TTo>(from);
public static TTo To<TFrom, TTo>(TFrom from) => Type<TFrom, TTo>(from);
[Obsolete("Renamed to Convert.Type for clarity")]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe TTo To<TFrom, TTo>(TFrom from, IFormatProvider provider) where TFrom : IConvertible
public static TTo To<TFrom, TTo>(TFrom from, IFormatProvider provider)
=> Type<TFrom, TTo>(from, provider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe TTo Type<TFrom, TTo>(TFrom from) where TFrom : IConvertible
public static unsafe TTo Type<TFrom, TTo>(TFrom from)
=> FunctionPointers.Convert<TFrom>.To<TTo>.Default(from, CultureInfo.InvariantCulture);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe TTo Type<TFrom, TTo>(TFrom from, IFormatProvider provider) where TFrom : IConvertible
public static unsafe TTo Type<TFrom, TTo>(TFrom from, IFormatProvider provider)
=> FunctionPointers.Convert<TFrom>.To<TTo>.Default(from, provider);
}
@@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Diagnostics.CodeAnalysis;
/// <summary>
/// Used to indicate a byref escapes and is not scoped.
/// </summary>
/// <remarks>
/// <para>
/// There are several cases where the C# compiler treats a <see langword="ref"/> as implicitly
/// <see langword="scoped"/> - where the compiler does not allow the <see langword="ref"/> to escape the method.
/// </para>
/// <para>
/// For example:
/// <list type="number">
/// <item><see langword="this"/> for <see langword="struct"/> instance methods.</item>
/// <item><see langword="ref"/> parameters that refer to <see langword="ref"/> <see langword="struct"/> types.</item>
/// <item><see langword="out"/> parameters.</item>
/// </list>
/// </para>
/// <para>
/// This attribute is used in those instances where the <see langword="ref"/> should be allowed to escape.
/// </para>
/// <para>
/// Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for
/// API authors to understand the lifetime implications of applying this attribute and how it may impact their users.
/// </para>
/// </remarks>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter,
Inherited = false)]
internal sealed class UnscopedRefAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="UnscopedRefAttribute"/> class.
/// </summary>
public UnscopedRefAttribute() { }
}
@@ -52,9 +52,7 @@ public static class TypeExtensions
{
// Primitive types use the keyword name
if (BuiltInTypesMap.TryGetValue(type, out var typeName))
{
return typeName!;
}
// Array types are displayed as Foo[]
if (type.IsArray)
@@ -62,7 +60,8 @@ public static class TypeExtensions
var elementType = type.GetElementType()!;
var rank = type.GetArrayRank();
return $"{FormatDisplayString(elementType, 0, elementType.GetGenericArguments())}[{new string(',', rank - 1)}]";
return $"{FormatDisplayString(elementType, 0,
elementType.GetGenericArguments())}[{new string(',', rank - 1)}]";
}
// By checking generic types here we are only interested in specific cases,
@@ -84,16 +83,17 @@ public static class TypeExtensions
}
// ValueTuple<T1, T2> types are displayed as (T1, T2)
if (genericTypeDefinition == typeof(ValueTuple<>) ||
genericTypeDefinition == typeof(ValueTuple<,>) ||
genericTypeDefinition == typeof(ValueTuple<,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,,,>) ||
genericTypeDefinition == typeof(ValueTuple<,,,,,,,>))
if (genericTypeDefinition == typeof(ValueTuple<>)
|| genericTypeDefinition == typeof(ValueTuple<,>)
|| genericTypeDefinition == typeof(ValueTuple<,,>)
|| genericTypeDefinition == typeof(ValueTuple<,,,>)
|| genericTypeDefinition == typeof(ValueTuple<,,,,>)
|| genericTypeDefinition == typeof(ValueTuple<,,,,,>)
|| genericTypeDefinition == typeof(ValueTuple<,,,,,,>)
|| genericTypeDefinition == typeof(ValueTuple<,,,,,,,>))
{
var formattedTypes = type.GetGenericArguments().Select(t => FormatDisplayString(t, 0, t.GetGenericArguments()));
var formattedTypes = type.GetGenericArguments()
.Select(static t => FormatDisplayString(t, 0, t.GetGenericArguments()));
return $"({string.Join(", ", formattedTypes)})";
}
@@ -109,7 +109,8 @@ public static class TypeExtensions
var genericArgumentsCount = int.Parse(tokens[1]);
var typeArgumentsOffset = typeArguments.Length - genericTypeOffset - genericArgumentsCount;
var currentTypeArguments = typeArguments.Slice(typeArgumentsOffset, genericArgumentsCount).ToArray();
var formattedTypes = currentTypeArguments.Select(t => FormatDisplayString(t, 0, t.GetGenericArguments()));
var formattedTypes
= currentTypeArguments.Select(static t => FormatDisplayString(t, 0, t.GetGenericArguments()));
// Standard generic types are displayed as Foo<T>
displayName = $"{tokens[0]}<{string.Join(", ", formattedTypes)}>";
@@ -130,7 +131,7 @@ public static class TypeExtensions
}
// Atomically get or build the display string for the current type.
return DisplayNames.GetValue(type, t =>
return DisplayNames.GetValue(type, static t =>
{
// By-ref types are displayed as T&
if (t.IsByRef)
@@ -14,13 +14,8 @@ public static class ValueTypeExtensions
/// <summary>
/// Gets the table of hex characters (doesn't allocate, maps to .text section, see <see href="https://github.com/dotnet/roslyn/pull/24621"/>).
/// </summary>
private static ReadOnlySpan<byte> HexCharactersTable => new[]
{
(byte)'0', (byte)'1', (byte)'2', (byte)'3',
(byte)'4', (byte)'5', (byte)'6', (byte)'7',
(byte)'8', (byte)'9', (byte)'A', (byte)'B',
(byte)'C', (byte)'D', (byte)'E', (byte)'F'
};
private static ReadOnlySpan<byte> HexCharactersTable
=> "0123456789ABCDEF"u8;
/// <summary>
/// Returns a hexadecimal <see cref="string"/> representation of a given <typeparamref name="T"/> value, left-padded and ordered as big-endian.
@@ -42,7 +37,7 @@ public static class ValueTypeExtensions
public static unsafe string ToHexString<T>(this T value)
where T : unmanaged
{
var sizeOfT = Unsafe.SizeOf<T>();
var sizeOfT = sizeof(T);
var bufferSize = (2 * sizeOfT) + 2;
var p = stackalloc char[bufferSize];
@@ -10,287 +10,287 @@ namespace PerformanceFish.Utility.Diagnostics;
partial class Guard
{
<#
GenerateTextForItems(EnumerableTypes, item =>
{
GenerateTextForItems(EnumerableTypes, item =>
{
#>
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must be empty.
/// Asserts that the input <#= item.XmlType #> instance must be empty.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is != 0.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is != 0.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsEmpty<T>(<#=item.Type#> <#=item.Name#>, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void IsEmpty<T>(<#= item.Type #> <#= item.Name #>, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> == 0)
if (<#= item.Name #>.<#= item.Size #> == 0)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsEmpty(<#=item.Cast#><#=item.Name#>, name);
ThrowHelper.ThrowArgumentExceptionForIsEmpty(<#= item.Cast #><#= item.Name #>, name);
}
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must not be empty.
/// Asserts that the input <#= item.XmlType #> instance must not be empty.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is == 0.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is == 0.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotEmpty<T>(<#=item.Type#> <#=item.Name#>, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void IsNotEmpty<T>(<#= item.Type #> <#= item.Name #>, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> != 0)
if (<#= item.Name #>.<#= item.Size #> != 0)
{
return;
}
<#
if (item.Type == "Span<T>")
{
if (item.Type == "Span<T>")
{
#>
ThrowHelper.ThrowArgumentExceptionForIsNotEmptyWithSpan<T>(name);
<#
}
else if (item.Type == "ReadOnlySpan<T>")
{
}
else if (item.Type == "ReadOnlySpan<T>")
{
#>
ThrowHelper.ThrowArgumentExceptionForIsNotEmptyWithReadOnlySpan<T>(name);
<#
}
else
{
}
else
{
#>
ThrowHelper.ThrowArgumentExceptionForIsNotEmpty<<#=item.Type#>>(name);
ThrowHelper.ThrowArgumentExceptionForIsNotEmpty<<#= item.Type #>>(name);
<#
}
}
#>
}
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must have a size of a specified value.
/// Asserts that the input <#= item.XmlType #> instance must have a size of a specified value.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="size">The target size to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is != <paramref name="size"/>.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is != <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void HasSizeEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> == size)
if (<#= item.Name #>.<#= item.Size #> == size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#=item.Cast#><#=item.Name#>, size, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#= item.Cast #><#= item.Name #>, size, name);
}
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must have a size not equal to a specified value.
/// Asserts that the input <#= item.XmlType #> instance must have a size not equal to a specified value.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="size">The target size to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is == <paramref name="size"/>.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is == <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeNotEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void HasSizeNotEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> != size)
if (<#= item.Name #>.<#= item.Size #> != size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(<#=item.Cast#><#=item.Name#>, size, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(<#= item.Cast #><#= item.Name #>, size, name);
}
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must have a size over a specified value.
/// Asserts that the input <#= item.XmlType #> instance must have a size over a specified value.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="size">The target size to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is &lt;= <paramref name="size"/>.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is &lt;= <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeGreaterThan<T>(<#=item.Type#> <#=item.Name#>, int size, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void HasSizeGreaterThan<T>(<#= item.Type #> <#= item.Name #>, int size, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> > size)
if (<#= item.Name #>.<#= item.Size #> > size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(<#=item.Cast#><#=item.Name#>, size, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(<#= item.Cast #><#= item.Name #>, size, name);
}
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must have a size of at least or equal to a specified value.
/// Asserts that the input <#= item.XmlType #> instance must have a size of at least or equal to a specified value.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="size">The target size to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is &lt; <paramref name="size"/>.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is &lt; <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeGreaterThanOrEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void HasSizeGreaterThanOrEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> >= size)
if (<#= item.Name #>.<#= item.Size #> >= size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(<#=item.Cast#><#=item.Name#>, size, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(<#= item.Cast #><#= item.Name #>, size, name);
}
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must have a size of less than a specified value.
/// Asserts that the input <#= item.XmlType #> instance must have a size of less than a specified value.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="size">The target size to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is >= <paramref name="size"/>.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is >= <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeLessThan<T>(<#=item.Type#> <#=item.Name#>, int size, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void HasSizeLessThan<T>(<#= item.Type #> <#= item.Name #>, int size, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> < size)
if (<#= item.Name #>.<#= item.Size #> < size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(<#=item.Cast#><#=item.Name#>, size, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(<#= item.Cast #><#= item.Name #>, size, name);
}
/// <summary>
/// Asserts that the input <#=item.XmlType#> instance must have a size of less than or equal to a specified value.
/// Asserts that the input <#= item.XmlType #> instance must have a size of less than or equal to a specified value.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to check the size for.</param>
/// <param name="size">The target size to test.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#=item.Name#>"/> is > <paramref name="size"/>.</exception>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="<#= item.Name #>"/> is > <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeLessThanOrEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, [CallerArgumentExpression("<#=item.Name#>")] string name = "")
public static void HasSizeLessThanOrEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, [CallerArgumentExpression("<#= item.Name #>")] string name = "")
{
if (<#=item.Name#>.<#=item.Size#> <= size)
if (<#= item.Name #>.<#= item.Size #> <= size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(<#=item.Cast#><#=item.Name#>, size, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(<#= item.Cast #><#= item.Name #>, size, name);
}
/// <summary>
/// Asserts that the source <#=item.XmlType#> instance must have the same size of a destination <#=item.XmlType#> instance.
/// Asserts that the source <#= item.XmlType #> instance must have the same size of a destination <#= item.XmlType #> instance.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="source">The source <#=item.XmlType#> instance to check the size for.</param>
/// <param name="destination">The destination <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="source">The source <#= item.XmlType #> instance to check the size for.</param>
/// <param name="destination">The destination <#= item.XmlType #> instance to check the size for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="source"/> is != the one of <paramref name="destination"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeEqualTo<T>(<#=item.Type#> source, <#=item.DestinationType#> destination, [CallerArgumentExpression("source")] string name = "")
public static void HasSizeEqualTo<T>(<#= item.Type #> source, <#= item.DestinationType #> destination, [CallerArgumentExpression("source")] string name = "")
{
if (source.<#=item.Size#> == destination.<#=item.Size#>)
if (source.<#= item.Size #> == destination.<#= item.Size #>)
{
return;
}
<#
if (item.HasCountProperty)
{
if (item.HasCountProperty)
{
#>
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#=item.Cast#>source, destination.<#=item.Size#>, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#= item.Cast #>source, destination.<#= item.Size #>, name);
<#
}
else
{
}
else
{
#>
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, <#=item.Cast#>destination, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, <#= item.Cast #>destination, name);
<#
}
}
#>
}
/// <summary>
/// Asserts that the source <#=item.XmlType#> instance must have a size of less than or equal to that of a destination <#=item.XmlType#> instance.
/// Asserts that the source <#= item.XmlType #> instance must have a size of less than or equal to that of a destination <#= item.XmlType #> instance.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="source">The source <#=item.XmlType#> instance to check the size for.</param>
/// <param name="destination">The destination <#=item.XmlType#> instance to check the size for.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="source">The source <#= item.XmlType #> instance to check the size for.</param>
/// <param name="destination">The destination <#= item.XmlType #> instance to check the size for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="source"/> is > the one of <paramref name="destination"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeLessThanOrEqualTo<T>(<#=item.Type#> source, <#=item.DestinationType#> destination, [CallerArgumentExpression("source")] string name = "")
public static void HasSizeLessThanOrEqualTo<T>(<#= item.Type #> source, <#= item.DestinationType #> destination, [CallerArgumentExpression("source")] string name = "")
{
if (source.<#=item.Size#> <= destination.<#=item.Size#>)
if (source.<#= item.Size #> <= destination.<#= item.Size #>)
{
return;
}
<#
if (item.HasCountProperty)
{
if (item.HasCountProperty)
{
#>
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#=item.Cast#>source, destination.<#=item.Size#>, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#= item.Cast #>source, destination.<#= item.Size #>, name);
<#
}
else
{
}
else
{
#>
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, <#=item.Cast#>destination, name);
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, <#= item.Cast #>destination, name);
<#
}
}
#>
}
/// <summary>
/// Asserts that the input index is valid for a given <#=item.XmlType#> instance.
/// Asserts that the input index is valid for a given <#= item.XmlType #> instance.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="index">The input index to be used to access <paramref name="<#=item.Name#>"/>.</param>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to use to validate <paramref name="index"/>.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="index">The input index to be used to access <paramref name="<#= item.Name #>"/>.</param>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to use to validate <paramref name="index"/>.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="index"/> is not valid to access <paramref name="<#=item.Name#>"/>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="index"/> is not valid to access <paramref name="<#= item.Name #>"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsInRangeFor<T>(int index, <#=item.Type#> <#=item.Name#>, [CallerArgumentExpression("index")] string name = "")
public static void IsInRangeFor<T>(int index, <#= item.Type #> <#= item.Name #>, [CallerArgumentExpression("index")] string name = "")
{
<#
// Here we're leveraging the fact that signed integers are represented
// in 2-complement to perform the bounds check with a single compare operation.
// This is the same trick used throughout CoreCLR as well.
// For more info and code sample, see the original conversation here:
// https://github.com/CommunityToolkit/WindowsCommunityToolkit/pull/3131#discussion_r390682835
// Here we're leveraging the fact that signed integers are represented
// in 2-complement to perform the bounds check with a single compare operation.
// This is the same trick used throughout CoreCLR as well.
// For more info and code sample, see the original conversation here:
// https://github.com/CommunityToolkit/WindowsCommunityToolkit/pull/3131#discussion_r390682835
#>
if ((uint)index < (uint)<#=item.Name#>.<#=item.Size#>)
if ((uint)index < (uint)<#= item.Name #>.<#= item.Size #>)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, <#=item.Cast#><#=item.Name#>, name);
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, <#= item.Cast #><#= item.Name #>, name);
}
/// <summary>
/// Asserts that the input index is not valid for a given <#=item.XmlType#> instance.
/// Asserts that the input index is not valid for a given <#= item.XmlType #> instance.
/// </summary>
/// <typeparam name="T">The item of items in the input <#=item.XmlType#> instance.</typeparam>
/// <param name="index">The input index to be used to access <paramref name="<#=item.Name#>"/>.</param>
/// <param name="<#=item.Name#>">The input <#=item.XmlType#> instance to use to validate <paramref name="index"/>.</param>
/// <typeparam name="T">The item of items in the input <#= item.XmlType #> instance.</typeparam>
/// <param name="index">The input index to be used to access <paramref name="<#= item.Name #>"/>.</param>
/// <param name="<#= item.Name #>">The input <#= item.XmlType #> instance to use to validate <paramref name="index"/>.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="index"/> is valid to access <paramref name="<#=item.Name#>"/>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="index"/> is valid to access <paramref name="<#= item.Name #>"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotInRangeFor<T>(int index, <#=item.Type#> <#=item.Name#>, [CallerArgumentExpression("index")] string name = "")
public static void IsNotInRangeFor<T>(int index, <#= item.Type #> <#= item.Name #>, [CallerArgumentExpression("index")] string name = "")
{
if ((uint)index >= (uint)<#=item.Name#>.<#=item.Size#>)
if ((uint)index >= (uint)<#= item.Name #>.<#= item.Size #>)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, <#=item.Cast#><#=item.Name#>, name);
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, <#= item.Cast #><#= item.Name #>, name);
}
<#
});
});
#>
}
@@ -10,19 +10,19 @@ namespace PerformanceFish.Utility.Diagnostics;
partial class Guard
{
<#
GenerateTextForItems(NumericTypes, typeInfo =>
{
var (type, prefix) = typeInfo;
GenerateTextForItems(NumericTypes, typeInfo =>
{
var (type, prefix) = typeInfo;
#>
/// <summary>
/// Asserts that the input value must be equal to a specified value.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="target">The target <see <#=prefix#>="<#=type#>"/> value to test for.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="target">The target <see <#= prefix #>="<#= type #>"/> value to test for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is != <paramref name="target"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsEqualTo(<#=type#> value, <#=type#> target, [CallerArgumentExpression("value")] string name = "")
public static void IsEqualTo(<#= type #> value, <#= type #> target, [CallerArgumentExpression("value")] string name = "")
{
if (value == target)
{
@@ -35,13 +35,13 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be not equal to a specified value.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="target">The target <see <#=prefix#>="<#=type#>"/> value to test for.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="target">The target <see <#= prefix #>="<#= type #>"/> value to test for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is == <paramref name="target"/>.</exception>
/// <remarks>The method is generic to avoid boxing the parameters, if they are value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotEqualTo(<#=type#> value, <#=type#> target, [CallerArgumentExpression("value")] string name = "")
public static void IsNotEqualTo(<#= type #> value, <#= type #> target, [CallerArgumentExpression("value")] string name = "")
{
if (value != target)
{
@@ -54,13 +54,13 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be less than a specified value.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="maximum">The exclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="maximum">The exclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is >= <paramref name="maximum"/>.</exception>
/// <remarks>The method is generic to avoid boxing the parameters, if they are value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsLessThan(<#=type#> value, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsLessThan(<#= type #> value, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value < maximum)
{
@@ -73,13 +73,13 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be less than or equal to a specified value.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="maximum">The inclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="maximum">The inclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is > <paramref name="maximum"/>.</exception>
/// <remarks>The method is generic to avoid boxing the parameters, if they are value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsLessThanOrEqualTo(<#=type#> value, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsLessThanOrEqualTo(<#= type #> value, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value <= maximum)
{
@@ -92,13 +92,13 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be greater than a specified value.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The exclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The exclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is &lt;= <paramref name="minimum"/>.</exception>
/// <remarks>The method is generic to avoid boxing the parameters, if they are value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsGreaterThan(<#=type#> value, <#=type#> minimum, [CallerArgumentExpression("value")] string name = "")
public static void IsGreaterThan(<#= type #> value, <#= type #> minimum, [CallerArgumentExpression("value")] string name = "")
{
if (value > minimum)
{
@@ -111,13 +111,13 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be greater than or equal to a specified value.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is &lt; <paramref name="minimum"/>.</exception>
/// <remarks>The method is generic to avoid boxing the parameters, if they are value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsGreaterThanOrEqualTo(<#=type#> value, <#=type#> minimum, [CallerArgumentExpression("value")] string name = "")
public static void IsGreaterThanOrEqualTo(<#= type #> value, <#= type #> minimum, [CallerArgumentExpression("value")] string name = "")
{
if (value >= minimum)
{
@@ -130,9 +130,9 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be in a given range.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is &lt; <paramref name="minimum"/> or >= <paramref name="maximum"/>.</exception>
/// <remarks>
@@ -140,7 +140,7 @@ var (type, prefix) = typeInfo;
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsInRange(<#=type#> value, <#=type#> minimum, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsInRange(<#= type #> value, <#= type #> minimum, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value >= minimum && value < maximum)
{
@@ -153,9 +153,9 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must not be in a given range.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is >= <paramref name="minimum"/> or &lt; <paramref name="maximum"/>.</exception>
/// <remarks>
@@ -163,7 +163,7 @@ var (type, prefix) = typeInfo;
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotInRange(<#=type#> value, <#=type#> minimum, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsNotInRange(<#= type #> value, <#= type #> minimum, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value < minimum || value >= maximum)
{
@@ -176,9 +176,9 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be in a given interval.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The exclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The exclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is &lt;= <paramref name="minimum"/> or >= <paramref name="maximum"/>.</exception>
/// <remarks>
@@ -186,7 +186,7 @@ var (type, prefix) = typeInfo;
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsBetween(<#=type#> value, <#=type#> minimum, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsBetween(<#= type #> value, <#= type #> minimum, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value > minimum && value < maximum)
{
@@ -199,9 +199,9 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must not be in a given interval.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The exclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The exclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="maximum">The exclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is > <paramref name="minimum"/> or &lt; <paramref name="maximum"/>.</exception>
/// <remarks>
@@ -209,7 +209,7 @@ var (type, prefix) = typeInfo;
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotBetween(<#=type#> value, <#=type#> minimum, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsNotBetween(<#= type #> value, <#= type #> minimum, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value <= minimum || value >= maximum)
{
@@ -222,9 +222,9 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must be in a given interval.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="maximum">The inclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="maximum">The inclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is &lt; <paramref name="minimum"/> or > <paramref name="maximum"/>.</exception>
/// <remarks>
@@ -232,7 +232,7 @@ var (type, prefix) = typeInfo;
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsBetweenOrEqualTo(<#=type#> value, <#=type#> minimum, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsBetweenOrEqualTo(<#= type #> value, <#= type #> minimum, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value >= minimum && value <= maximum)
{
@@ -245,9 +245,9 @@ var (type, prefix) = typeInfo;
/// <summary>
/// Asserts that the input value must not be in a given interval.
/// </summary>
/// <param name="value">The input <see <#=prefix#>="<#=type#>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="maximum">The inclusive maximum <see <#=prefix#>="<#=type#>"/> value that is accepted.</param>
/// <param name="value">The input <see <#= prefix #>="<#= type #>"/> value to test.</param>
/// <param name="minimum">The inclusive minimum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="maximum">The inclusive maximum <see <#= prefix #>="<#= type #>"/> value that is accepted.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is >= <paramref name="minimum"/> or &lt;= <paramref name="maximum"/>.</exception>
/// <remarks>
@@ -255,7 +255,7 @@ var (type, prefix) = typeInfo;
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotBetweenOrEqualTo(<#=type#> value, <#=type#> minimum, <#=type#> maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsNotBetweenOrEqualTo(<#= type #> value, <#= type #> minimum, <#= type #> maximum, [CallerArgumentExpression("value")] string name = "")
{
if (value < minimum || value > maximum)
{
@@ -265,6 +265,6 @@ var (type, prefix) = typeInfo;
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name);
}
<#
});
});
#>
}
@@ -1,17 +1,29 @@
# T4 templates and generated APIs
This folder contains a number of template files (with the `.tt` or `.ttinclude` extensions) and the generated `.g.cs` files generated by those templates. The template files use the T4 format, which is natively supported by Visual Studio (more info is available [here](https://docs.microsoft.com/visualstudio/modeling/code-generation-and-t4-text-templates)).
This folder contains a number of template files (with the `.tt` or `.ttinclude` extensions) and the generated `.g.cs`
files generated by those templates. The template files use the T4 format, which is natively supported by Visual Studio (
more info is available [here](https://docs.microsoft.com/visualstudio/modeling/code-generation-and-t4-text-templates)).
## Why is this needed?
There are a few reasons why T4 templates are used for the `Guard` class:
- Especially when many overloads are available for an API, using templates makes it much easier to maintain code and spot mistakes, as the actual number of lines of code to review is much smaller: it's just the same code from each template!
- Using type-specific overloads instead of generic methods can result in faster code. For instance, T4 templates are used to generate overloads for comparison APIs (eg. `Guard.IsGreaterThan(int, int, string)`). This results in more compact and optimized code as opposed to a generic method using `where T : IComparable<T>` as type constraint.
- In some cases, using generic methods just isn't possible. For instance, types like `Span<T>` and `ReadOnlySpan<T>` can't be used as generic type parameters, and even if that had been possible, they don't implement an interface we could use in the generic type constraint. Using T4 templates solves this issue, as we can just have specialized method for each supported type or collection type.
- Especially when many overloads are available for an API, using templates makes it much easier to maintain code and
spot mistakes, as the actual number of lines of code to review is much smaller: it's just the same code from each
template!
- Using type-specific overloads instead of generic methods can result in faster code. For instance, T4 templates are
used to generate overloads for comparison APIs (eg. `Guard.IsGreaterThan(int, int, string)`). This results in more
compact and optimized code as opposed to a generic method using `where T : IComparable<T>` as type constraint.
- In some cases, using generic methods just isn't possible. For instance, types like `Span<T>` and `ReadOnlySpan<T>`
can't be used as generic type parameters, and even if that had been possible, they don't implement an interface we
could use in the generic type constraint. Using T4 templates solves this issue, as we can just have specialized method
for each supported type or collection type.
## How to make changes
If you need to change an API that is declared in a template, or to add a new one, just edit the right `.tt` file and save it: Visual Studio will take care of updating the generated `.g.cs` file automatically. Don't make changes to those generated `.g.cs` files directly, as those will be overwritten as soon as their source template is updated.
If you need to change an API that is declared in a template, or to add a new one, just edit the right `.tt` file and
save it: Visual Studio will take care of updating the generated `.g.cs` file automatically. Don't make changes to those
generated `.g.cs` files directly, as those will be overwritten as soon as their source template is updated.
Note that all the `.g.cs` files are checked in into the repository, so if you do make changes to a template file, make sure to also include the updated `.g.cs` file in your commits.
Note that all the `.g.cs` files are checked in into the repository, so if you do make changes to a template file, make
sure to also include the updated `.g.cs` file in your commits.
@@ -13,109 +13,109 @@ partial class Guard
partial class ThrowHelper
{
<#
GenerateTextForItems(EnumerableTypes, item =>
{
GenerateTextForItems(EnumerableTypes, item =>
{
#>
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.IsEmpty{T}(T[],string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsEmpty<T>(<#=item.Type#> <#=item.Name#>, string name)
public static void ThrowArgumentExceptionForIsEmpty<T>(<#= item.Type #> <#= item.Name #>, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must be empty, had a size of {AssertString(<#=item.Name#>.<#=item.Size#>)}.", name);
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must be empty, had a size of {AssertString(<#= item.Name #>.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeEqualTo{T}(T[],int,string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, string name)
public static void ThrowArgumentExceptionForHasSizeEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(<#=item.Name#>.<#=item.Size#>)}.", name);
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(<#= item.Name #>.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeNotEqualTo{T}(T[],int,string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeNotEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, string name)
public static void ThrowArgumentExceptionForHasSizeNotEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(<#=item.Name#>.<#=item.Size#>)}.", name);
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(<#= item.Name #>.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeGreaterThan{T}(T[],int,string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeGreaterThan<T>(<#=item.Type#> <#=item.Name#>, int size, string name)
public static void ThrowArgumentExceptionForHasSizeGreaterThan<T>(<#= item.Type #> <#= item.Name #>, int size, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size over {size}, had a size of {AssertString(<#=item.Name#>.<#=item.Size#>)}.", name);
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size over {size}, had a size of {AssertString(<#= item.Name #>.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeGreaterThanOrEqualTo{T}(T[],int,string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, string name)
public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(<#=item.Name#>.<#=item.Size#>)}.", name);
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(<#= item.Name #>.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeLessThan{T}(T[],int,string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeLessThan<T>(<#=item.Type#> <#=item.Name#>, int size, string name)
public static void ThrowArgumentExceptionForHasSizeLessThan<T>(<#= item.Type #> <#= item.Name #>, int size, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(<#=item.Name#>.<#=item.Size#>)}.", name);
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(<#= item.Name #>.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeLessThanOrEqualTo{T}(T[],int,string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo<T>(<#=item.Type#> <#=item.Name#>, int size, string name)
public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo<T>(<#= item.Type #> <#= item.Name #>, int size, string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(<#=item.Name#>.<#=item.Size#>)}.", name);
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(<#= item.Name #>.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeEqualTo{T}(T[],T[],string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeEqualTo<T>(<#=item.Type#> source, <#=item.DestinationType#> destination, string name)
public static void ThrowArgumentExceptionForHasSizeEqualTo<T>(<#= item.Type #> source, <#= item.DestinationType #> destination, string name)
{
throw new ArgumentException($"The source {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size equal to {AssertString(destination.<#=item.Size#>)} (the destination), had a size of {AssertString(source.<#=item.Size#>)}.", name);
throw new ArgumentException($"The source {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size equal to {AssertString(destination.<#= item.Size #>)} (the destination), had a size of {AssertString(source.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="Guard.HasSizeLessThanOrEqualTo{T}(T[],T[],string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo<T>(<#=item.Type#> source, <#=item.DestinationType#> destination, string name)
public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo<T>(<#= item.Type #> source, <#= item.DestinationType #> destination, string name)
{
throw new ArgumentException($"The source {AssertString(name)} ({typeof(<#=item.Type#>).ToTypeString()}) must have a size less than or equal to {AssertString(destination.<#=item.Size#>)} (the destination), had a size of {AssertString(source.<#=item.Size#>)}.", name);
throw new ArgumentException($"The source {AssertString(name)} ({typeof(<#= item.Type #>).ToTypeString()}) must have a size less than or equal to {AssertString(destination.<#= item.Size #>)} (the destination), had a size of {AssertString(source.<#= item.Size #>)}.", name);
}
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="Guard.IsInRangeFor{T}(int,T[],string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor<T>(int index, <#=item.Type#> <#=item.Name#>, string name)
public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor<T>(int index, <#= item.Type #> <#= item.Name #>, string name)
{
throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(<#=item.Name#>.<#=item.Size#>)} to be a valid index for the target collection ({typeof(<#=item.Type#>).ToTypeString()}), was {AssertString(index)}.");
throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(<#= item.Name #>.<#= item.Size #>)} to be a valid index for the target collection ({typeof(<#= item.Type #>).ToTypeString()}), was {AssertString(index)}.");
}
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="Guard.IsNotInRangeFor{T}(int,T[],string)"/> (or an overload) fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor<T>(int index, <#=item.Type#> <#=item.Name#>, string name)
public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor<T>(int index, <#= item.Type #> <#= item.Name #>, string name)
{
throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(<#=item.Name#>.<#=item.Size#>)} to be an invalid index for the target collection ({typeof(<#=item.Type#>).ToTypeString()}), was {AssertString(index)}.");
throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(<#= item.Name #>.<#= item.Size #>)} to be an invalid index for the target collection ({typeof(<#= item.Type #>).ToTypeString()}), was {AssertString(index)}.");
}
<#
});
});
#>
}
}
@@ -71,11 +71,14 @@
new EnumerableTypeInfo("Span<T>", "<see cref=\"Span{T}\"/>", "span", "Length", "Span<T>", ""),
new EnumerableTypeInfo("ReadOnlySpan<T>", "<see cref=\"ReadOnlySpan{T}\"/>", "span", "Length", "Span<T>", ""),
new EnumerableTypeInfo("Memory<T>", "<see cref=\"Memory{T}\"/>", "memory", "Length", "Memory<T>", ""),
new EnumerableTypeInfo("ReadOnlyMemory<T>", "<see cref=\"ReadOnlyMemory{T}\"/>", "memory", "Length", "Memory<T>", ""),
new EnumerableTypeInfo("ReadOnlyMemory<T>", "<see cref=\"ReadOnlyMemory{T}\"/>", "memory", "Length", "Memory<T>",
""),
new EnumerableTypeInfo("T[]", "<see typeparamref=\"T\"/> array", "array", "Length", "T[]", ""),
new EnumerableTypeInfo("List<T>", "<see cref=\"List{T}\"/>", "list", "Count", "List<T>", "(ICollection<T>)"),
new EnumerableTypeInfo("ICollection<T>", "<see cref=\"ICollection{T}\"/>", "collection", "Count", "ICollection<T>", ""),
new EnumerableTypeInfo("IReadOnlyCollection<T>", "<see cref=\"IReadOnlyCollection{T}\"/>", "collection", "Count", "ICollection<T>", ""),
new EnumerableTypeInfo("ICollection<T>", "<see cref=\"ICollection{T}\"/>", "collection", "Count",
"ICollection<T>", ""),
new EnumerableTypeInfo("IReadOnlyCollection<T>", "<see cref=\"IReadOnlyCollection{T}\"/>", "collection", "Count",
"ICollection<T>", "")
};
/// <summary>
@@ -107,10 +110,11 @@
for (int i = 0; i < items.Count; i++)
{
// Insert a blank line after the first item
if (i > 0) WriteLine("");
if (i > 0)
WriteLine("");
// Invoke the factory with the current item
factory(items [i]);
factory(items[i]);
}
}
#>
+4 -10
View File
@@ -20,9 +20,7 @@ public static partial class Guard
public static void IsTrue([DoesNotReturnIf(false)] bool value, [CallerArgumentExpression("value")] string name = "")
{
if (value)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsTrue(name);
}
@@ -38,9 +36,7 @@ public static partial class Guard
public static void IsTrue([DoesNotReturnIf(false)] bool value, string name, string message)
{
if (value)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsTrue(name, message);
}
@@ -55,9 +51,7 @@ public static partial class Guard
public static void IsFalse([DoesNotReturnIf(true)] bool value, [CallerArgumentExpression("value")] string name = "")
{
if (!value)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsFalse(name);
}
@@ -73,9 +67,7 @@ public static partial class Guard
public static void IsFalse([DoesNotReturnIf(true)] bool value, string name, string message)
{
if (!value)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsFalse(name, message);
}
@@ -155,7 +147,8 @@ public static partial class Guard
{
StringBuilder builder = new();
this.handler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder);
this.handler =
new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder);
this.builder = builder;
shouldAppend = true;
@@ -273,7 +266,8 @@ public static partial class Guard
{
StringBuilder builder = new();
this.handler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder);
this.handler =
new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder);
this.builder = builder;
shouldAppend = true;
@@ -21,9 +21,7 @@ public partial class Guard
where T : struct, IEquatable<T>
{
if (value.Equals(default))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsDefault(value, name);
}
@@ -40,9 +38,7 @@ public partial class Guard
where T : struct, IEquatable<T>
{
if (!value.Equals(default))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotDefault<T>(name);
}
@@ -61,9 +57,7 @@ public partial class Guard
where T : notnull, IEquatable<T>
{
if (value.Equals(target))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name);
}
@@ -82,9 +76,7 @@ public partial class Guard
where T : notnull, IEquatable<T>
{
if (!value.Equals(target))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name);
}
@@ -98,7 +90,8 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is not a bitwise match for <paramref name="target"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void IsBitwiseEqualTo<T>(T value, T target, [CallerArgumentExpression("value")] string name = "")
public static unsafe void IsBitwiseEqualTo<T>(T value, T target,
[CallerArgumentExpression("value")] string name = "")
where T : unmanaged
{
// Include some fast paths if the input type is of size 1, 2, 4, 8, or 16.
@@ -112,9 +105,7 @@ public partial class Guard
var targetByte = Unsafe.As<T, byte>(ref target);
if (valueByte == targetByte)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name);
}
@@ -124,9 +115,7 @@ public partial class Guard
var targetUShort = Unsafe.As<T, ushort>(ref target);
if (valueUShort == targetUShort)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name);
}
@@ -136,9 +125,7 @@ public partial class Guard
var targetUInt = Unsafe.As<T, uint>(ref target);
if (valueUInt == targetUInt)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name);
}
@@ -148,9 +135,7 @@ public partial class Guard
var targetULong = Unsafe.As<T, ulong>(ref target);
if (Bit64Compare(ref valueULong, ref targetULong))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name);
}
@@ -165,9 +150,7 @@ public partial class Guard
var targetULong1 = Unsafe.Add(ref Unsafe.As<T, ulong>(ref target), 1);
if (Bit64Compare(ref valueULong1, ref targetULong1))
{
return;
}
}
ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name);
@@ -178,9 +161,7 @@ public partial class Guard
Span<byte> targetBytes = new(Unsafe.AsPointer(ref target), sizeof(T));
if (valueBytes.SequenceEqual(targetBytes))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name);
}
@@ -196,8 +177,7 @@ public partial class Guard
ref var r0 = ref Unsafe.As<ulong, int>(ref left);
ref var r1 = ref Unsafe.As<ulong, int>(ref right);
return r0 == r1 &&
Unsafe.Add(ref r0, 1) == Unsafe.Add(ref r1, 1);
return r0 == r1 && Unsafe.Add(ref r0, 1) == Unsafe.Add(ref r1, 1);
}
return left == right;
@@ -217,9 +197,7 @@ public partial class Guard
where T : notnull, IComparable<T>
{
if (value.CompareTo(maximum) < 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name);
}
@@ -238,9 +216,7 @@ public partial class Guard
where T : notnull, IComparable<T>
{
if (value.CompareTo(maximum) <= 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name);
}
@@ -259,9 +235,7 @@ public partial class Guard
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) > 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name);
}
@@ -276,13 +250,12 @@ public partial class Guard
/// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="value"/> is &lt; <paramref name="minimum"/>.</exception>
/// <remarks>The method is generic to avoid boxing the parameters, if they are value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsGreaterThanOrEqualTo<T>(T value, T minimum, [CallerArgumentExpression("value")] string name = "")
public static void IsGreaterThanOrEqualTo<T>(T value, T minimum,
[CallerArgumentExpression("value")] string name = "")
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) >= 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name);
}
@@ -305,9 +278,7 @@ public partial class Guard
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) >= 0 && value.CompareTo(maximum) < 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name);
}
@@ -326,13 +297,12 @@ public partial class Guard
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotInRange<T>(T value, T minimum, T maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsNotInRange<T>(T value, T minimum, T maximum,
[CallerArgumentExpression("value")] string name = "")
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) < 0 || value.CompareTo(maximum) >= 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name);
}
@@ -355,9 +325,7 @@ public partial class Guard
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) > 0 && value.CompareTo(maximum) < 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name);
}
@@ -376,13 +344,12 @@ public partial class Guard
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotBetween<T>(T value, T minimum, T maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsNotBetween<T>(T value, T minimum, T maximum,
[CallerArgumentExpression("value")] string name = "")
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) <= 0 || value.CompareTo(maximum) >= 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name);
}
@@ -401,13 +368,12 @@ public partial class Guard
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsBetweenOrEqualTo<T>(T value, T minimum, T maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsBetweenOrEqualTo<T>(T value, T minimum, T maximum,
[CallerArgumentExpression("value")] string name = "")
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) >= 0 && value.CompareTo(maximum) <= 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name);
}
@@ -426,13 +392,12 @@ public partial class Guard
/// The method is generic to avoid boxing the parameters, if they are value types.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotBetweenOrEqualTo<T>(T value, T minimum, T maximum, [CallerArgumentExpression("value")] string name = "")
public static void IsNotBetweenOrEqualTo<T>(T value, T minimum, T maximum,
[CallerArgumentExpression("value")] string name = "")
where T : notnull, IComparable<T>
{
if (value.CompareTo(minimum) < 0 || value.CompareTo(maximum) > 0)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name);
}
@@ -18,14 +18,13 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) > <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsCloseTo(int value, int target, uint delta, [CallerArgumentExpression("value")] string name = "")
public static void IsCloseTo(int value, int target, uint delta,
[CallerArgumentExpression("value")] string name = "")
{
var difference = value >= target ? (uint)(value - target) : (uint)(target - value);
if (difference <= delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name);
}
@@ -39,14 +38,13 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) &lt;= <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotCloseTo(int value, int target, uint delta, [CallerArgumentExpression("value")] string name = "")
public static void IsNotCloseTo(int value, int target, uint delta,
[CallerArgumentExpression("value")] string name = "")
{
var difference = value >= target ? (uint)(value - target) : (uint)(target - value);
if (difference > delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name);
}
@@ -60,14 +58,13 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) > <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsCloseTo(long value, long target, ulong delta, [CallerArgumentExpression("value")] string name = "")
public static void IsCloseTo(long value, long target, ulong delta,
[CallerArgumentExpression("value")] string name = "")
{
var difference = value >= target ? (ulong)(value - target) : (ulong)(target - value);
if (difference <= delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name);
}
@@ -81,14 +78,13 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) &lt;= <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotCloseTo(long value, long target, ulong delta, [CallerArgumentExpression("value")] string name = "")
public static void IsNotCloseTo(long value, long target, ulong delta,
[CallerArgumentExpression("value")] string name = "")
{
var difference = value >= target ? (ulong)(value - target) : (ulong)(target - value);
if (difference > delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name);
}
@@ -102,12 +98,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) > <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsCloseTo(float value, float target, float delta, [CallerArgumentExpression("value")] string name = "")
public static void IsCloseTo(float value, float target, float delta,
[CallerArgumentExpression("value")] string name = "")
{
if (Math.Abs(value - target) <= delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name);
}
@@ -121,12 +116,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) &lt;= <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotCloseTo(float value, float target, float delta, [CallerArgumentExpression("value")] string name = "")
public static void IsNotCloseTo(float value, float target, float delta,
[CallerArgumentExpression("value")] string name = "")
{
if (Math.Abs(value - target) > delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name);
}
@@ -140,12 +134,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) > <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsCloseTo(double value, double target, double delta, [CallerArgumentExpression("value")] string name = "")
public static void IsCloseTo(double value, double target, double delta,
[CallerArgumentExpression("value")] string name = "")
{
if (Math.Abs(value - target) <= delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name);
}
@@ -159,12 +152,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) &lt;= <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotCloseTo(double value, double target, double delta, [CallerArgumentExpression("value")] string name = "")
public static void IsNotCloseTo(double value, double target, double delta,
[CallerArgumentExpression("value")] string name = "")
{
if (Math.Abs(value - target) > delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name);
}
@@ -178,14 +170,13 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) > <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsCloseTo(nint value, nint target, nuint delta, [CallerArgumentExpression("value")] string name = "")
public static void IsCloseTo(nint value, nint target, nuint delta,
[CallerArgumentExpression("value")] string name = "")
{
var difference = value >= target ? (nuint)(value - target) : (nuint)(target - value);
if (difference <= delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name);
}
@@ -199,14 +190,13 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if (<paramref name="value"/> - <paramref name="target"/>) &lt;= <paramref name="delta"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotCloseTo(nint value, nint target, nuint delta, [CallerArgumentExpression("value")] string name = "")
public static void IsNotCloseTo(nint value, nint target, nuint delta,
[CallerArgumentExpression("value")] string name = "")
{
var difference = value >= target ? (nuint)(value - target) : (nuint)(target - value);
if (difference > delta)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name);
}
@@ -20,9 +20,7 @@ public partial class Guard
public static void CanRead(Stream stream, [CallerArgumentExpression("stream")] string name = "")
{
if (stream.CanRead)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForCanRead(stream, name);
}
@@ -37,9 +35,7 @@ public partial class Guard
public static void CanWrite(Stream stream, [CallerArgumentExpression("stream")] string name = "")
{
if (stream.CanWrite)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForCanWrite(stream, name);
}
@@ -54,9 +50,7 @@ public partial class Guard
public static void CanSeek(Stream stream, [CallerArgumentExpression("stream")] string name = "")
{
if (stream.CanSeek)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForCanSeek(stream, name);
}
@@ -71,9 +65,7 @@ public partial class Guard
public static void IsAtStartPosition(Stream stream, [CallerArgumentExpression("stream")] string name = "")
{
if (stream.Position == 0)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsAtStartPosition(stream, name);
}
+10 -41
View File
@@ -22,9 +22,7 @@ public partial class Guard
public static void IsNullOrEmpty(string? text, [CallerArgumentExpression("text")] string name = "")
{
if (string.IsNullOrEmpty(text))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNullOrEmpty(text, name);
}
@@ -40,9 +38,7 @@ public partial class Guard
public static void IsNotNullOrEmpty([NotNull] string? text, [CallerArgumentExpression("text")] string name = "")
{
if (!string.IsNullOrEmpty(text))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotNullOrEmpty(text, name);
}
@@ -57,9 +53,7 @@ public partial class Guard
public static void IsNullOrWhiteSpace(string? text, [CallerArgumentExpression("text")] string name = "")
{
if (string.IsNullOrWhiteSpace(text))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNullOrWhiteSpace(text, name);
}
@@ -72,12 +66,11 @@ public partial class Guard
/// <exception cref="ArgumentNullException">Thrown if <paramref name="text"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">Thrown if <paramref name="text"/> is whitespace.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotNullOrWhiteSpace([NotNull] string? text, [CallerArgumentExpression("text")] string name = "")
public static void IsNotNullOrWhiteSpace([NotNull] string? text,
[CallerArgumentExpression("text")] string name = "")
{
if (!string.IsNullOrWhiteSpace(text))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotNullOrWhiteSpace(text, name);
}
@@ -92,9 +85,7 @@ public partial class Guard
public static void IsEmpty(string text, [CallerArgumentExpression("text")] string name = "")
{
if (text.Length == 0)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsEmpty(text, name);
}
@@ -109,9 +100,7 @@ public partial class Guard
public static void IsNotEmpty(string text, [CallerArgumentExpression("text")] string name = "")
{
if (text.Length != 0)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotEmpty(name);
}
@@ -126,9 +115,7 @@ public partial class Guard
public static void IsWhiteSpace(string text, [CallerArgumentExpression("text")] string name = "")
{
if (string.IsNullOrWhiteSpace(text))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsWhiteSpace(text, name);
}
@@ -143,9 +130,7 @@ public partial class Guard
public static void IsNotWhiteSpace(string text, [CallerArgumentExpression("text")] string name = "")
{
if (!string.IsNullOrWhiteSpace(text))
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotWhiteSpace(text, name);
}
@@ -161,9 +146,7 @@ public partial class Guard
public static void HasSizeEqualTo(string text, int size, [CallerArgumentExpression("text")] string name = "")
{
if (text.Length == size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(text, size, name);
}
@@ -179,9 +162,7 @@ public partial class Guard
public static void HasSizeNotEqualTo(string text, int size, [CallerArgumentExpression("text")] string name = "")
{
if (text.Length != size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(text, size, name);
}
@@ -197,9 +178,7 @@ public partial class Guard
public static void HasSizeGreaterThan(string text, int size, [CallerArgumentExpression("text")] string name = "")
{
if (text.Length > size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(text, size, name);
}
@@ -212,12 +191,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="text"/> is &lt; <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeGreaterThanOrEqualTo(string text, int size, [CallerArgumentExpression("text")] string name = "")
public static void HasSizeGreaterThanOrEqualTo(string text, int size,
[CallerArgumentExpression("text")] string name = "")
{
if (text.Length >= size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(text, size, name);
}
@@ -233,9 +211,7 @@ public partial class Guard
public static void HasSizeLessThan(string text, int size, [CallerArgumentExpression("text")] string name = "")
{
if (text.Length < size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(text, size, name);
}
@@ -248,12 +224,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="text"/> is > <paramref name="size"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeLessThanOrEqualTo(string text, int size, [CallerArgumentExpression("text")] string name = "")
public static void HasSizeLessThanOrEqualTo(string text, int size,
[CallerArgumentExpression("text")] string name = "")
{
if (text.Length <= size)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(text, size, name);
}
@@ -267,12 +242,11 @@ public partial class Guard
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="text"/> is != the one of <paramref name="destination"/>.</exception>
/// <remarks>The <see cref="string"/> type is immutable, but the name of this API is kept for consistency with the other overloads.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeEqualTo(string text, string destination, [CallerArgumentExpression("text")] string name = "")
public static void HasSizeEqualTo(string text, string destination,
[CallerArgumentExpression("text")] string name = "")
{
if (text.Length == destination.Length)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(text, destination, name);
}
@@ -286,12 +260,11 @@ public partial class Guard
/// <exception cref="ArgumentException">Thrown if the size of <paramref name="text"/> is > the one of <paramref name="destination"/>.</exception>
/// <remarks>The <see cref="string"/> type is immutable, but the name of this API is kept for consistency with the other overloads.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasSizeLessThanOrEqualTo(string text, string destination, [CallerArgumentExpression("text")] string name = "")
public static void HasSizeLessThanOrEqualTo(string text, string destination,
[CallerArgumentExpression("text")] string name = "")
{
if (text.Length <= destination.Length)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(text, destination, name);
}
@@ -307,9 +280,7 @@ public partial class Guard
public static void IsInRangeFor(int index, string text, [CallerArgumentExpression("index")] string name = "")
{
if ((uint)index < (uint)text.Length)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, text, name);
}
@@ -325,9 +296,7 @@ public partial class Guard
public static void IsNotInRangeFor(int index, string text, [CallerArgumentExpression("index")] string name = "")
{
if ((uint)index >= (uint)text.Length)
{
return;
}
ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, text, name);
}
+4 -22
View File
@@ -20,9 +20,7 @@ public partial class Guard
public static void IsCompleted(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (task.IsCompleted)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCompleted(task, name);
}
@@ -37,9 +35,7 @@ public partial class Guard
public static void IsNotCompleted(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (!task.IsCompleted)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCompleted(task, name);
}
@@ -54,9 +50,7 @@ public partial class Guard
public static void IsCompletedSuccessfully(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (task.Status == TaskStatus.RanToCompletion)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCompletedSuccessfully(task, name);
}
@@ -71,9 +65,7 @@ public partial class Guard
public static void IsNotCompletedSuccessfully(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (task.Status != TaskStatus.RanToCompletion)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCompletedSuccessfully(task, name);
}
@@ -88,9 +80,7 @@ public partial class Guard
public static void IsFaulted(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (task.IsFaulted)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsFaulted(task, name);
}
@@ -105,9 +95,7 @@ public partial class Guard
public static void IsNotFaulted(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (!task.IsFaulted)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotFaulted(task, name);
}
@@ -122,9 +110,7 @@ public partial class Guard
public static void IsCanceled(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (task.IsCanceled)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsCanceled(task, name);
}
@@ -139,9 +125,7 @@ public partial class Guard
public static void IsNotCanceled(Task task, [CallerArgumentExpression("task")] string name = "")
{
if (!task.IsCanceled)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForIsNotCanceled(task, name);
}
@@ -154,12 +138,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="task"/> doesn't match <paramref name="status"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasStatusEqualTo(Task task, TaskStatus status, [CallerArgumentExpression("task")] string name = "")
public static void HasStatusEqualTo(Task task, TaskStatus status,
[CallerArgumentExpression("task")] string name = "")
{
if (task.Status == status)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasStatusEqualTo(task, status, name);
}
@@ -172,12 +155,11 @@ public partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="task"/> matches <paramref name="status"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void HasStatusNotEqualTo(Task task, TaskStatus status, [CallerArgumentExpression("task")] string name = "")
public static void HasStatusNotEqualTo(Task task, TaskStatus status,
[CallerArgumentExpression("task")] string name = "")
{
if (task.Status != status)
{
return;
}
ThrowHelper.ThrowArgumentExceptionForHasStatusNotEqualTo(task, status, name);
}
+62 -15
View File
@@ -3,8 +3,9 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute;
namespace FisheryLib.Utility.Diagnostics;
@@ -12,6 +13,7 @@ namespace FisheryLib.Utility.Diagnostics;
/// Helper methods to verify conditions when running code.
/// </summary>
[DebuggerStepThrough]
[PublicAPI]
public static partial class Guard
{
/// <summary>
@@ -22,7 +24,7 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is not <see langword="null"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNull<T>(T? value, [CallerArgumentExpression("value")] string name = "")
public static void IsNull<T>([NoEnumeration] T? value, [CallerArgumentExpression("value")] string name = "")
{
if (value is null)
return;
@@ -38,7 +40,7 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is not <see langword="null"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNull<T>(T? value, [CallerArgumentExpression("value")] string name = "")
public static void IsNull<T>([NoEnumeration] T? value, [CallerArgumentExpression("value")] string name = "")
where T : struct
{
if (value is null)
@@ -55,7 +57,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see langword="null"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotNull<T>([NotNull] T? value, [CallerArgumentExpression("value")] string name = "")
public static void IsNotNull<T>([NoEnumeration] [NotNull] T? value,
[CallerArgumentExpression("value")] string name = "")
{
if (value is not null)
return;
@@ -71,7 +74,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is <see langword="null"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotNull<T>([NotNull] T? value, [CallerArgumentExpression("value")] string name = "")
public static void IsNotNull<T>([NoEnumeration] [NotNull] T? value,
[CallerArgumentExpression("value")] string name = "")
where T : struct
{
if (value is not null)
@@ -88,7 +92,7 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is not of type <typeparamref name="T"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsOfType<T>(object value, [CallerArgumentExpression("value")] string name = "")
public static void IsOfType<T>([NoEnumeration] object value, [CallerArgumentExpression("value")] string name = "")
{
if (value.GetType() == typeof(T))
return;
@@ -104,7 +108,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is of type <typeparamref name="T"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotOfType<T>(object value, [CallerArgumentExpression("value")] string name = "")
public static void IsNotOfType<T>([NoEnumeration] object value,
[CallerArgumentExpression("value")] string name = "")
{
if (value.GetType() != typeof(T))
return;
@@ -120,7 +125,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the type of <paramref name="value"/> is not the same as <paramref name="type"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsOfType(object value, Type type, [CallerArgumentExpression("value")] string name = "")
public static void IsOfType([NoEnumeration] object value, Type type,
[CallerArgumentExpression("value")] string name = "")
{
if (value.GetType() == type)
return;
@@ -136,7 +142,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if the type of <paramref name="value"/> is the same as <paramref name="type"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotOfType(object value, Type type, [CallerArgumentExpression("value")] string name = "")
public static void IsNotOfType([NoEnumeration] object value, Type type,
[CallerArgumentExpression("value")] string name = "")
{
if (value.GetType() != type)
return;
@@ -152,7 +159,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> can't be assigned to type <typeparamref name="T"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsAssignableToType<T>(object value, [CallerArgumentExpression("value")] string name = "")
public static void IsAssignableToType<T>([NoEnumeration] object value,
[CallerArgumentExpression("value")] string name = "")
{
if (value is T)
return;
@@ -168,7 +176,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> can be assigned to type <typeparamref name="T"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotAssignableToType<T>(object value, [CallerArgumentExpression("value")] string name = "")
public static void IsNotAssignableToType<T>([NoEnumeration] object value,
[CallerArgumentExpression("value")] string name = "")
{
if (value is not T)
return;
@@ -184,7 +193,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> can't be assigned to <paramref name="type"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsAssignableToType(object value, Type type, [CallerArgumentExpression("value")] string name = "")
public static void IsAssignableToType([NoEnumeration] object value, Type type,
[CallerArgumentExpression("value")] string name = "")
{
if (type.IsInstanceOfType(value))
return;
@@ -200,7 +210,8 @@ public static partial class Guard
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> can be assigned to <paramref name="type"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsNotAssignableToType(object value, Type type, [CallerArgumentExpression("value")] string name = "")
public static void IsNotAssignableToType([NoEnumeration] object value, Type type,
[CallerArgumentExpression("value")] string name = "")
{
if (!type.IsInstanceOfType(value))
return;
@@ -208,6 +219,40 @@ public static partial class Guard
ThrowHelper.ThrowArgumentExceptionForIsNotAssignableToType(value, type, name);
}
/// <summary>
/// Asserts that the input type can be assigned to a specified type.
/// </summary>
/// <param name="value">The input <see cref="Type"/> to test.</param>
/// <param name="type">The type to look for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> can't be assigned to <paramref name="type"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsTypeAssignableToType([NoEnumeration] Type value, Type type,
[CallerArgumentExpression("value")] string name = "")
{
if (type.IsAssignableFrom(value))
return;
ThrowHelper.ThrowArgumentExceptionForIsAssignableToType(value, type, name);
}
/// <summary>
/// Asserts that the input type can't be assigned to a specified type.
/// </summary>
/// <param name="value">The input <see cref="Type"/> to test.</param>
/// <param name="type">The type to look for.</param>
/// <param name="name">The name of the input parameter being tested.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> can be assigned to <paramref name="type"/>.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsTypeNotAssignableToType([NoEnumeration] Type value, Type type,
[CallerArgumentExpression("value")] string name = "")
{
if (!type.IsAssignableFrom(value))
return;
ThrowHelper.ThrowArgumentExceptionForIsNotAssignableToType(value, type, name);
}
/// <summary>
/// Asserts that the input value must be the same instance as the target value.
/// </summary>
@@ -218,7 +263,8 @@ public static partial class Guard
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is not the same instance as <paramref name="target"/>.</exception>
/// <remarks>The method is generic to prevent using it with value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsReferenceEqualTo<T>(T value, T target, [CallerArgumentExpression("value")] string name = "")
public static void IsReferenceEqualTo<T>([NoEnumeration] T value, T target,
[CallerArgumentExpression("value")] string name = "")
where T : class
{
if (ReferenceEquals(value, target))
@@ -237,7 +283,8 @@ public static partial class Guard
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is the same instance as <paramref name="target"/>.</exception>
/// <remarks>The method is generic to prevent using it with value types.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void IsReferenceNotEqualTo<T>(T value, T target, [CallerArgumentExpression("value")] string name = "")
public static void IsReferenceNotEqualTo<T>([NoEnumeration] T value, T target,
[CallerArgumentExpression("value")] string name = "")
where T : class
{
if (!ReferenceEquals(value, target))
@@ -19,9 +19,8 @@ public partial class Guard
/// <remarks>This method is needed because <see cref="Span{T}"/> can't be used as a generic type parameter.</remarks>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotEmptyWithSpan<T>(string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span<T>).ToTypeString()}) must not be empty.", name);
}
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(Span<T>).ToTypeString()}) must not be empty.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotEmpty{T}(ReadOnlySpan{T},string)"/> fails.
@@ -30,9 +29,8 @@ public partial class Guard
/// <remarks>This method is needed because <see cref="ReadOnlySpan{T}"/> can't be used as a generic type parameter.</remarks>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotEmptyWithReadOnlySpan<T>(string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan<T>).ToTypeString()}) must not be empty.", name);
}
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(ReadOnlySpan<T>).ToTypeString()}) must not be empty.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotEmpty{T}(T[],string)"/> (or an overload) fails.
@@ -40,8 +38,7 @@ public partial class Guard
/// <typeparam name="T">The item of items in the input collection.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotEmpty<T>(string name)
{
throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be empty.", name);
}
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be empty.", name);
}
}
@@ -18,7 +18,10 @@ public partial class Guard
/// <typeparam name="T">The type of <see langword="struct"/> value type being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsDefault<T>(T value, string name)
where T : struct => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be the default value {AssertString(default(T))}, was {AssertString(value)}.", name);
where T : struct
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be the default value {AssertString(default(T))}, was {AssertString(value)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotDefault{T}"/> fails.
@@ -26,21 +29,30 @@ public partial class Guard
/// <typeparam name="T">The type of <see langword="struct"/> value type being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotDefault<T>(string name)
where T : struct => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be the default value {AssertString(default(T))}.", name);
where T : struct
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be the default value {AssertString(default(T))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsEqualTo{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsEqualTo<T>(T value, T target, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be equal to {AssertString(target)}, was {AssertString(value)}.", name);
public static void ThrowArgumentExceptionForIsEqualTo<T>(T value, T target, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be equal to {AssertString(target)}, was {AssertString(value)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotEqualTo{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotEqualTo<T>(T value, T target, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be equal to {AssertString(target)}, was {AssertString(value)}.", name);
public static void ThrowArgumentExceptionForIsNotEqualTo<T>(T value, T target, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be equal to {AssertString(target)}, was {AssertString(value)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsBitwiseEqualTo{T}"/> fails.
@@ -48,76 +60,103 @@ public partial class Guard
/// <typeparam name="T">The type of input values being compared.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForBitwiseEqualTo<T>(T value, T target, string name)
where T : unmanaged => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) is not a bitwise match, was <{value.ToHexString()}> instead of <{target.ToHexString()}>.", name);
where T : unmanaged
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) is not a bitwise match, was <{value.ToHexString()}> instead of <{target.ToHexString()}>.",
name);
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsLessThan{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsLessThan<T>(T value, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be less than {AssertString(maximum)}, was {AssertString(value)}.");
public static void ThrowArgumentOutOfRangeExceptionForIsLessThan<T>(T value, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be less than {AssertString(maximum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsLessThanOrEqualTo{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo<T>(T value, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be less than or equal to {AssertString(maximum)}, was {AssertString(value)}.");
public static void ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo<T>(T value, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be less than or equal to {AssertString(maximum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsGreaterThan{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsGreaterThan<T>(T value, T minimum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be greater than {AssertString(minimum)}, was {AssertString(value)}.");
public static void ThrowArgumentOutOfRangeExceptionForIsGreaterThan<T>(T value, T minimum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be greater than {AssertString(minimum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsGreaterThanOrEqualTo{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo<T>(T value, T minimum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be greater than or equal to {AssertString(minimum)}, was {AssertString(value)}.");
public static void ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo<T>(T value, T minimum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be greater than or equal to {AssertString(minimum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsInRange{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsInRange<T>(T value, T minimum, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be in the range given by {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
public static void ThrowArgumentOutOfRangeExceptionForIsInRange<T>(T value, T minimum, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be in the range given by {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsInRange{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsNotInRange<T>(T value, T minimum, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be in the range given by {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
public static void
ThrowArgumentOutOfRangeExceptionForIsNotInRange<T>(T value, T minimum, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be in the range given by {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsBetween{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsBetween<T>(T value, T minimum, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be between {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
public static void ThrowArgumentOutOfRangeExceptionForIsBetween<T>(T value, T minimum, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be between {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsNotBetween{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsNotBetween<T>(T value, T minimum, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be between {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
public static void
ThrowArgumentOutOfRangeExceptionForIsNotBetween<T>(T value, T minimum, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be between {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsBetweenOrEqualTo{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo<T>(T value, T minimum, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be between or equal to {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
public static void
ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo<T>(T value, T minimum, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be between or equal to {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsNotBetweenOrEqualTo{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of values being tested.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo<T>(T value, T minimum, T maximum, string name) => throw new ArgumentOutOfRangeException(name, value!, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be between or equal to {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
public static void
ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo<T>(T value, T minimum, T maximum, string name)
=> throw new ArgumentOutOfRangeException(name, value!,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be between or equal to {AssertString(minimum)} and {AssertString(maximum)}, was {AssertString(value)}.");
}
}
@@ -16,60 +16,90 @@ public partial class Guard
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCloseTo(int,int,uint,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCloseTo(int value, int target, uint delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(int).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((double)((long)value - target)))}.", name);
public static void ThrowArgumentExceptionForIsCloseTo(int value, int target, uint delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(int).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((double)((long)value - target)))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCloseTo(int,int,uint,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCloseTo(int value, int target, uint delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(int).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((double)((long)value - target)))}.", name);
public static void ThrowArgumentExceptionForIsNotCloseTo(int value, int target, uint delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(int).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((double)((long)value - target)))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCloseTo(long,long,ulong,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCloseTo(long value, long target, ulong delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(long).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((decimal)value - target))}.", name);
public static void ThrowArgumentExceptionForIsCloseTo(long value, long target, ulong delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(long).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((decimal)value - target))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCloseTo(long,long,ulong,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCloseTo(long value, long target, ulong delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(long).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((decimal)value - target))}.", name);
public static void ThrowArgumentExceptionForIsNotCloseTo(long value, long target, ulong delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(long).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs((decimal)value - target))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCloseTo(float,float,float,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCloseTo(float value, float target, float delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(float).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.", name);
public static void ThrowArgumentExceptionForIsCloseTo(float value, float target, float delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(float).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCloseTo(float,float,float,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCloseTo(float value, float target, float delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(float).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.", name);
public static void ThrowArgumentExceptionForIsNotCloseTo(float value, float target, float delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(float).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCloseTo(double,double,double,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCloseTo(double value, double target, double delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(double).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.", name);
public static void ThrowArgumentExceptionForIsCloseTo(double value, double target, double delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(double).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCloseTo(double,double,double,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCloseTo(double value, double target, double delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(double).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.", name);
public static void ThrowArgumentExceptionForIsNotCloseTo(double value, double target, double delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(double).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCloseTo(nint,nint,nuint,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCloseTo(nint value, nint target, nuint delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(nint).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.", name);
public static void ThrowArgumentExceptionForIsCloseTo(nint value, nint target, nuint delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(nint).ToTypeString()}) must be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCloseTo(nint,nint,nuint,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCloseTo(nint value, nint target, nuint delta, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(nint).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.", name);
public static void ThrowArgumentExceptionForIsNotCloseTo(nint value, nint target, nuint delta, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(nint).ToTypeString()}) must not be within a distance of {AssertString(delta)} from {AssertString(target)}, was {AssertString(value)} and had a distance of {AssertString(Math.Abs(value - target))}.",
name);
}
}
@@ -17,24 +17,33 @@ public partial class Guard
/// Throws an <see cref="ArgumentException"/> when <see cref="CanRead"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForCanRead(Stream stream, string name) => throw new ArgumentException($"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support reading.", name);
public static void ThrowArgumentExceptionForCanRead(Stream stream, string name)
=> throw new ArgumentException(
$"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support reading.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="CanWrite"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForCanWrite(Stream stream, string name) => throw new ArgumentException($"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support writing.", name);
public static void ThrowArgumentExceptionForCanWrite(Stream stream, string name)
=> throw new ArgumentException(
$"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support writing.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="CanSeek"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForCanSeek(Stream stream, string name) => throw new ArgumentException($"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support seeking.", name);
public static void ThrowArgumentExceptionForCanSeek(Stream stream, string name)
=> throw new ArgumentException(
$"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support seeking.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsAtStartPosition"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsAtStartPosition(Stream stream, string name) => throw new ArgumentException($"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) must be at position {AssertString(0)}, was at {AssertString(stream.Position)}.", name);
public static void ThrowArgumentExceptionForIsAtStartPosition(Stream stream, string name)
=> throw new ArgumentException(
$"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) must be at position {AssertString(0)}, was at {AssertString(stream.Position)}.",
name);
}
}
@@ -18,7 +18,8 @@ public partial class Guard
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNullOrEmpty(string? text, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must be null or empty, was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must be null or empty, was {AssertString(text)}.", name);
/// <summary>
/// Throws an <see cref="ArgumentNullException"/> or <see cref="ArgumentException"/> when <see cref="IsNotNullOrEmpty"/> fails.
@@ -29,8 +30,10 @@ public partial class Guard
[MethodImpl(MethodImplOptions.NoInlining)]
static Exception GetException(string? text, string name)
=> text is null
? new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or empty, was null.")
: new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or empty, was empty.", name);
? new ArgumentNullException(name,
$"Parameter {AssertString(name)} (string) must not be null or empty, was null.")
: new ArgumentException(
$"Parameter {AssertString(name)} (string) must not be null or empty, was empty.", name);
throw GetException(text, name);
}
@@ -40,7 +43,8 @@ public partial class Guard
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNullOrWhiteSpace(string? text, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must be null or whitespace, was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must be null or whitespace, was {AssertString(text)}.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotNullOrWhiteSpace"/> fails.
@@ -51,8 +55,11 @@ public partial class Guard
[MethodImpl(MethodImplOptions.NoInlining)]
static Exception GetException(string? text, string name)
=> text is null
? new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or whitespace, was null.")
: new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or whitespace, was whitespace.", name);
? new ArgumentNullException(name,
$"Parameter {AssertString(name)} (string) must not be null or whitespace, was null.")
: new ArgumentException(
$"Parameter {AssertString(name)} (string) must not be null or whitespace, was whitespace.",
name);
throw GetException(text, name);
}
@@ -62,7 +69,8 @@ public partial class Guard
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsEmpty(string text, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must be empty, was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must be empty, was {AssertString(text)}.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotEmpty"/> fails.
@@ -76,83 +84,104 @@ public partial class Guard
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsWhiteSpace(string text, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must be whitespace, was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must be whitespace, was {AssertString(text)}.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotWhiteSpace"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotWhiteSpace(string text, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must not be whitespace, was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must not be whitespace, was {AssertString(text)}.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeEqualTo(string,int,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeEqualTo(string text, int size, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must have a size equal to {size}, had a size of {text.Length} and was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must have a size equal to {size}, had a size of {text.Length} and was {AssertString(text)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeNotEqualTo"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeNotEqualTo(string text, int size, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must not have a size equal to {size}, was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must not have a size equal to {size}, was {AssertString(text)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeGreaterThan"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeGreaterThan(string text, int size, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must have a size over {size}, had a size of {text.Length} and was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must have a size over {size}, had a size of {text.Length} and was {AssertString(text)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeGreaterThanOrEqualTo"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(string text, int size, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must have a size of at least {size}, had a size of {text.Length} and was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must have a size of at least {size}, had a size of {text.Length} and was {AssertString(text)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeLessThan"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeLessThan(string text, int size, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must have a size less than {size}, had a size of {text.Length} and was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must have a size less than {size}, had a size of {text.Length} and was {AssertString(text)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeLessThanOrEqualTo(string,int,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(string text, int size, string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} (string) must have a size less than or equal to {size}, had a size of {text.Length} and was {AssertString(text)}.", name);
=> throw new ArgumentException(
$"Parameter {AssertString(name)} (string) must have a size less than or equal to {size}, had a size of {text.Length} and was {AssertString(text)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeEqualTo(string,string,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeEqualTo(string source, string destination, string name)
=> throw new ArgumentException($"The source {AssertString(name)} (string) must have a size equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}.", name);
=> throw new ArgumentException(
$"The source {AssertString(name)} (string) must have a size equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasSizeLessThanOrEqualTo(string,string,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(string source, string destination, string name)
=> throw new ArgumentException($"The source {AssertString(name)} (string) must have a size less than or equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}.", name);
public static void
ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(string source, string destination, string name)
=> throw new ArgumentException(
$"The source {AssertString(name)} (string) must have a size less than or equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsInRangeFor(int,string,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, string text, string name)
=> throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(text.Length)} to be a valid index for the target string, was {AssertString(index)}.");
=> throw new ArgumentOutOfRangeException(name, index,
$"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(text.Length)} to be a valid index for the target string, was {AssertString(index)}.");
/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when <see cref="IsNotInRangeFor(int,string,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, string text, string name)
=> throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(text.Length)} to be an invalid index for the target string, was {AssertString(index)}.");
=> throw new ArgumentOutOfRangeException(name, index,
$"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(text.Length)} to be an invalid index for the target string, was {AssertString(index)}.");
}
}
@@ -17,60 +17,90 @@ public partial class Guard
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCompleted"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCompleted(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be completed, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsCompleted(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be completed, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCompleted"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCompleted(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be completed, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsNotCompleted(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be completed, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCompletedSuccessfully"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCompletedSuccessfully(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be completed successfully, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsCompletedSuccessfully(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be completed successfully, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCompletedSuccessfully"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCompletedSuccessfully(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be completed successfully, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsNotCompletedSuccessfully(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be completed successfully, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsFaulted"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsFaulted(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be faulted, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsFaulted(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be faulted, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotFaulted"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotFaulted(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be faulted, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsNotFaulted(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be faulted, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsCanceled"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsCanceled(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be canceled, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsCanceled(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must be canceled, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotCanceled"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotCanceled(Task task, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be canceled, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForIsNotCanceled(Task task, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not be canceled, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasStatusEqualTo"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasStatusEqualTo(Task task, TaskStatus status, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must have status {status}, had status {AssertString(task.Status)}.", name);
public static void ThrowArgumentExceptionForHasStatusEqualTo(Task task, TaskStatus status, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must have status {status}, had status {AssertString(task.Status)}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="HasStatusNotEqualTo"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForHasStatusNotEqualTo(Task task, TaskStatus status, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not have status {AssertString(status)}.", name);
public static void ThrowArgumentExceptionForHasStatusNotEqualTo(Task task, TaskStatus status, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({task.GetType().ToTypeString()}) must not have status {AssertString(status)}.",
name);
}
}
@@ -32,7 +32,10 @@ public partial class Guard
/// </summary>
/// <typeparam name="T">The type of the input value.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNull<T>(T value, string name) => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be null, was {AssertString(value)} ({value!.GetType().ToTypeString()}).", name);
public static void ThrowArgumentExceptionForIsNull<T>(T value, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be null, was {AssertString(value)} ({value!.GetType().ToTypeString()}).",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNull{T}(T,string)"/> (where <typeparamref name="T"/> is <see langword="struct"/>) fails.
@@ -40,66 +43,95 @@ public partial class Guard
/// <typeparam name="T">The type of the input value.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNull<T>(T? value, string name)
where T : struct => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T?).ToTypeString()}) must be null, was {AssertString(value)} ({typeof(T).ToTypeString()}).", name);
where T : struct
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T?).ToTypeString()}) must be null, was {AssertString(value)} ({typeof(T).ToTypeString()}).",
name);
/// <summary>
/// Throws an <see cref="ArgumentNullException"/> when <see cref="IsNotNull{T}(T,string)"/> fails.
/// </summary>
/// <typeparam name="T">The type of the input value.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentNullExceptionForIsNotNull<T>(string name) => throw new ArgumentNullException(name, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be not null).");
public static void ThrowArgumentNullExceptionForIsNotNull<T>(string name)
=> throw new ArgumentNullException(name,
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be not null).");
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsOfType{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of the input value.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsOfType<T>(object value, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must be of type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsOfType<T>(object value, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must be of type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotOfType{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type of the input value.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotOfType<T>(object value, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must not be of type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsNotOfType<T>(object value, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must not be of type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsOfType"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsOfType(object value, Type type, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must be of type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsOfType(object value, Type type, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must be of type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotOfType"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotOfType(object value, Type type, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must not be of type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsNotOfType(object value, Type type, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must not be of type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsAssignableToType{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type being checked against.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsAssignableToType<T>(object value, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must be assignable to type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsAssignableToType<T>(object value, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must be assignable to type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsNotAssignableToType{T}"/> fails.
/// </summary>
/// <typeparam name="T">The type being checked against.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotAssignableToType<T>(object value, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must not be assignable to type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsNotAssignableToType<T>(object value, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must not be assignable to type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsAssignableToType"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsAssignableToType(object value, Type type, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must be assignable to type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsAssignableToType(object value, Type type, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must be assignable to type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsAssignableToType"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsNotAssignableToType(object value, Type type, string name) => throw new ArgumentException($"Parameter {AssertString(name)} must not be assignable to type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.", name);
public static void ThrowArgumentExceptionForIsNotAssignableToType(object value, Type type, string name)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must not be assignable to type {type.ToTypeString()}, was {value.GetType().ToTypeString()}.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsReferenceEqualTo{T}"/> fails.
@@ -107,7 +139,10 @@ public partial class Guard
/// <typeparam name="T">The type of input value being compared.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsReferenceEqualTo<T>(string name)
where T : class => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be the same instance as the target object.", name);
where T : class
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be the same instance as the target object.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsReferenceNotEqualTo{T}"/> fails.
@@ -115,30 +150,39 @@ public partial class Guard
/// <typeparam name="T">The type of input value being compared.</typeparam>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsReferenceNotEqualTo<T>(string name)
where T : class => throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be the same instance as the target object.", name);
where T : class
=> throw new ArgumentException(
$"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be the same instance as the target object.",
name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsTrue(bool,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsTrue(string name) => throw new ArgumentException($"Parameter {AssertString(name)} must be true, was false.", name);
public static void ThrowArgumentExceptionForIsTrue(string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} must be true, was false.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsTrue(bool,string,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsTrue(string name, string message) => throw new ArgumentException($"Parameter {AssertString(name)} must be true, was false: {AssertString(message)}.", name);
public static void ThrowArgumentExceptionForIsTrue(string name, string message)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must be true, was false: {AssertString(message)}.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsFalse(bool,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsFalse(string name) => throw new ArgumentException($"Parameter {AssertString(name)} must be false, was true.", name);
public static void ThrowArgumentExceptionForIsFalse(string name)
=> throw new ArgumentException($"Parameter {AssertString(name)} must be false, was true.", name);
/// <summary>
/// Throws an <see cref="ArgumentException"/> when <see cref="IsFalse(bool,string,string)"/> fails.
/// </summary>
[DoesNotReturn]
public static void ThrowArgumentExceptionForIsFalse(string name, string message) => throw new ArgumentException($"Parameter {AssertString(name)} must be false, was true: {AssertString(message)}.", name);
public static void ThrowArgumentExceptionForIsFalse(string name, string message)
=> throw new ArgumentException(
$"Parameter {AssertString(name)} must be false, was true: {AssertString(message)}.", name);
}
}
@@ -30,7 +30,8 @@ public static partial class ThrowHelper
/// <exception cref="ArrayTypeMismatchException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArrayTypeMismatchException<T>(string? message) => throw new ArrayTypeMismatchException(message);
public static T ThrowArrayTypeMismatchException<T>(string? message)
=> throw new ArrayTypeMismatchException(message);
/// <summary>
/// Throws a new <see cref="ArrayTypeMismatchException"/>.
@@ -41,7 +42,8 @@ public static partial class ThrowHelper
/// <exception cref="ArrayTypeMismatchException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArrayTypeMismatchException<T>(string? message, Exception? innerException) => throw new ArrayTypeMismatchException(message, innerException);
public static T ThrowArrayTypeMismatchException<T>(string? message, Exception? innerException)
=> throw new ArrayTypeMismatchException(message, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentException"/>.
@@ -71,7 +73,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentException<T>(string? message, Exception? innerException) => throw new ArgumentException(message, innerException);
public static T ThrowArgumentException<T>(string? message, Exception? innerException)
=> throw new ArgumentException(message, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentException"/>.
@@ -82,7 +85,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentException<T>(string? name, string? message) => throw new ArgumentException(message, name);
public static T ThrowArgumentException<T>(string? name, string? message)
=> throw new ArgumentException(message, name);
/// <summary>
/// Throws a new <see cref="ArgumentException"/>.
@@ -94,7 +98,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentException<T>(string? name, string? message, Exception? innerException) => throw new ArgumentException(message, name, innerException);
public static T ThrowArgumentException<T>(string? name, string? message, Exception? innerException)
=> throw new ArgumentException(message, name, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentNullException"/>.
@@ -124,7 +129,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentNullException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentNullException<T>(string? name, Exception? innerException) => throw new ArgumentNullException(name, innerException);
public static T ThrowArgumentNullException<T>(string? name, Exception? innerException)
=> throw new ArgumentNullException(name, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentNullException"/>.
@@ -135,7 +141,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentNullException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentNullException<T>(string? name, string? message) => throw new ArgumentNullException(name, message);
public static T ThrowArgumentNullException<T>(string? name, string? message)
=> throw new ArgumentNullException(name, message);
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/>.
@@ -165,7 +172,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentOutOfRangeException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentOutOfRangeException<T>(string? name, Exception? innerException) => throw new ArgumentOutOfRangeException(name, innerException);
public static T ThrowArgumentOutOfRangeException<T>(string? name, Exception? innerException)
=> throw new ArgumentOutOfRangeException(name, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/>.
@@ -176,7 +184,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentOutOfRangeException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentOutOfRangeException<T>(string? name, string? message) => throw new ArgumentOutOfRangeException(name, message);
public static T ThrowArgumentOutOfRangeException<T>(string? name, string? message)
=> throw new ArgumentOutOfRangeException(name, message);
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/>.
@@ -188,7 +197,8 @@ public static partial class ThrowHelper
/// <exception cref="ArgumentOutOfRangeException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowArgumentOutOfRangeException<T>(string? name, object? value, string? message) => throw new ArgumentOutOfRangeException(name, value, message);
public static T ThrowArgumentOutOfRangeException<T>(string? name, object? value, string? message)
=> throw new ArgumentOutOfRangeException(name, value, message);
/// <summary>
/// Throws a new <see cref="COMException"/>.
@@ -218,7 +228,8 @@ public static partial class ThrowHelper
/// <exception cref="COMException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowCOMException<T>(string? message, Exception? innerException) => throw new COMException(message, innerException);
public static T ThrowCOMException<T>(string? message, Exception? innerException)
=> throw new COMException(message, innerException);
/// <summary>
/// Throws a new <see cref="COMException"/>.
@@ -259,7 +270,8 @@ public static partial class ThrowHelper
/// <exception cref="ExternalException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowExternalException<T>(string? message, Exception? innerException) => throw new ExternalException(message, innerException);
public static T ThrowExternalException<T>(string? message, Exception? innerException)
=> throw new ExternalException(message, innerException);
/// <summary>
/// Throws a new <see cref="ExternalException"/>.
@@ -270,7 +282,8 @@ public static partial class ThrowHelper
/// <exception cref="ExternalException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowExternalException<T>(string? message, int error) => throw new ExternalException(message, error);
public static T ThrowExternalException<T>(string? message, int error)
=> throw new ExternalException(message, error);
/// <summary>
/// Throws a new <see cref="FormatException"/>.
@@ -300,7 +313,8 @@ public static partial class ThrowHelper
/// <exception cref="FormatException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowFormatException<T>(string? message, Exception? innerException) => throw new FormatException(message, innerException);
public static T ThrowFormatException<T>(string? message, Exception? innerException)
=> throw new FormatException(message, innerException);
/// <summary>
/// Throws a new <see cref="InsufficientMemoryException"/>.
@@ -319,7 +333,8 @@ public static partial class ThrowHelper
/// <exception cref="InsufficientMemoryException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowInsufficientMemoryException<T>(string? message) => throw new InsufficientMemoryException(message);
public static T ThrowInsufficientMemoryException<T>(string? message)
=> throw new InsufficientMemoryException(message);
/// <summary>
/// Throws a new <see cref="InsufficientMemoryException"/>.
@@ -330,7 +345,8 @@ public static partial class ThrowHelper
/// <exception cref="InsufficientMemoryException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowInsufficientMemoryException<T>(string? message, Exception? innerException) => throw new InsufficientMemoryException(message, innerException);
public static T ThrowInsufficientMemoryException<T>(string? message, Exception? innerException)
=> throw new InsufficientMemoryException(message, innerException);
/// <summary>
/// Throws a new <see cref="InvalidDataException"/>.
@@ -360,7 +376,8 @@ public static partial class ThrowHelper
/// <exception cref="InvalidDataException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowInvalidDataException<T>(string? message, Exception? innerException) => throw new InvalidDataException(message, innerException);
public static T ThrowInvalidDataException<T>(string? message, Exception? innerException)
=> throw new InvalidDataException(message, innerException);
/// <summary>
/// Throws a new <see cref="InvalidOperationException"/>.
@@ -390,7 +407,8 @@ public static partial class ThrowHelper
/// <exception cref="InvalidOperationException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowInvalidOperationException<T>(string? message, Exception? innerException) => throw new InvalidOperationException(message, innerException);
public static T ThrowInvalidOperationException<T>(string? message, Exception? innerException)
=> throw new InvalidOperationException(message, innerException);
/// <summary>
/// Throws a new <see cref="LockRecursionException"/>.
@@ -420,7 +438,8 @@ public static partial class ThrowHelper
/// <exception cref="LockRecursionException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowLockRecursionException<T>(string? message, Exception? innerException) => throw new LockRecursionException(message, innerException);
public static T ThrowLockRecursionException<T>(string? message, Exception? innerException)
=> throw new LockRecursionException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingFieldException"/>.
@@ -450,7 +469,8 @@ public static partial class ThrowHelper
/// <exception cref="MissingFieldException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowMissingFieldException<T>(string? message, Exception? innerException) => throw new MissingFieldException(message, innerException);
public static T ThrowMissingFieldException<T>(string? message, Exception? innerException)
=> throw new MissingFieldException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingFieldException"/>.
@@ -461,7 +481,8 @@ public static partial class ThrowHelper
/// <exception cref="MissingFieldException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowMissingFieldException<T>(string? className, string? fieldName) => throw new MissingFieldException(className, fieldName);
public static T ThrowMissingFieldException<T>(string? className, string? fieldName)
=> throw new MissingFieldException(className, fieldName);
/// <summary>
/// Throws a new <see cref="MissingMemberException"/>.
@@ -491,7 +512,8 @@ public static partial class ThrowHelper
/// <exception cref="MissingMemberException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowMissingMemberException<T>(string? message, Exception? innerException) => throw new MissingMemberException(message, innerException);
public static T ThrowMissingMemberException<T>(string? message, Exception? innerException)
=> throw new MissingMemberException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingMemberException"/>.
@@ -502,7 +524,8 @@ public static partial class ThrowHelper
/// <exception cref="MissingMemberException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowMissingMemberException<T>(string? className, string? memberName) => throw new MissingMemberException(className, memberName);
public static T ThrowMissingMemberException<T>(string? className, string? memberName)
=> throw new MissingMemberException(className, memberName);
/// <summary>
/// Throws a new <see cref="MissingMethodException"/>.
@@ -532,7 +555,8 @@ public static partial class ThrowHelper
/// <exception cref="MissingMethodException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowMissingMethodException<T>(string? message, Exception? innerException) => throw new MissingMethodException(message, innerException);
public static T ThrowMissingMethodException<T>(string? message, Exception? innerException)
=> throw new MissingMethodException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingMethodException"/>.
@@ -543,7 +567,8 @@ public static partial class ThrowHelper
/// <exception cref="MissingMethodException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowMissingMethodException<T>(string? className, string? methodName) => throw new MissingMethodException(className, methodName);
public static T ThrowMissingMethodException<T>(string? className, string? methodName)
=> throw new MissingMethodException(className, methodName);
/// <summary>
/// Throws a new <see cref="NotSupportedException"/>.
@@ -573,7 +598,8 @@ public static partial class ThrowHelper
/// <exception cref="NotSupportedException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowNotSupportedException<T>(string? message, Exception? innerException) => throw new NotSupportedException(message, innerException);
public static T ThrowNotSupportedException<T>(string? message, Exception? innerException)
=> throw new NotSupportedException(message, innerException);
/// <summary>
/// Throws a new <see cref="ObjectDisposedException"/>.
@@ -583,7 +609,8 @@ public static partial class ThrowHelper
/// <exception cref="ObjectDisposedException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowObjectDisposedException<T>(string? objectName) => throw new ObjectDisposedException(objectName);
public static T ThrowObjectDisposedException<T>(string? objectName)
=> throw new ObjectDisposedException(objectName);
/// <summary>
/// Throws a new <see cref="ObjectDisposedException"/>.
@@ -594,7 +621,8 @@ public static partial class ThrowHelper
/// <exception cref="ObjectDisposedException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowObjectDisposedException<T>(string? objectName, Exception? innerException) => throw new ObjectDisposedException(objectName, innerException);
public static T ThrowObjectDisposedException<T>(string? objectName, Exception? innerException)
=> throw new ObjectDisposedException(objectName, innerException);
/// <summary>
/// Throws a new <see cref="ObjectDisposedException"/>.
@@ -605,7 +633,8 @@ public static partial class ThrowHelper
/// <exception cref="ObjectDisposedException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowObjectDisposedException<T>(string? objectName, string? message) => throw new ObjectDisposedException(objectName, message);
public static T ThrowObjectDisposedException<T>(string? objectName, string? message)
=> throw new ObjectDisposedException(objectName, message);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -624,7 +653,8 @@ public static partial class ThrowHelper
/// <exception cref="OperationCanceledException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowOperationCanceledException<T>(string? message) => throw new OperationCanceledException(message);
public static T ThrowOperationCanceledException<T>(string? message)
=> throw new OperationCanceledException(message);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -635,7 +665,8 @@ public static partial class ThrowHelper
/// <exception cref="OperationCanceledException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowOperationCanceledException<T>(string? message, Exception? innerException) => throw new OperationCanceledException(message, innerException);
public static T ThrowOperationCanceledException<T>(string? message, Exception? innerException)
=> throw new OperationCanceledException(message, innerException);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -645,7 +676,8 @@ public static partial class ThrowHelper
/// <exception cref="OperationCanceledException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowOperationCanceledException<T>(CancellationToken token) => throw new OperationCanceledException(token);
public static T ThrowOperationCanceledException<T>(CancellationToken token)
=> throw new OperationCanceledException(token);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -656,7 +688,8 @@ public static partial class ThrowHelper
/// <exception cref="OperationCanceledException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowOperationCanceledException<T>(string? message, CancellationToken token) => throw new OperationCanceledException(message, token);
public static T ThrowOperationCanceledException<T>(string? message, CancellationToken token)
=> throw new OperationCanceledException(message, token);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -668,7 +701,9 @@ public static partial class ThrowHelper
/// <exception cref="OperationCanceledException">Thrown with the specified parameters.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowOperationCanceledException<T>(string? message, Exception? innerException, CancellationToken token) => throw new OperationCanceledException(message, innerException, token);
public static T ThrowOperationCanceledException<T>(string? message, Exception? innerException,
CancellationToken token)
=> throw new OperationCanceledException(message, innerException, token);
/// <summary>
/// Throws a new <see cref="PlatformNotSupportedException"/>.
@@ -687,7 +722,8 @@ public static partial class ThrowHelper
/// <exception cref="PlatformNotSupportedException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowPlatformNotSupportedException<T>(string? message) => throw new PlatformNotSupportedException(message);
public static T ThrowPlatformNotSupportedException<T>(string? message)
=> throw new PlatformNotSupportedException(message);
/// <summary>
/// Throws a new <see cref="PlatformNotSupportedException"/>.
@@ -698,7 +734,8 @@ public static partial class ThrowHelper
/// <exception cref="PlatformNotSupportedException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowPlatformNotSupportedException<T>(string? message, Exception? innerException) => throw new PlatformNotSupportedException(message, innerException);
public static T ThrowPlatformNotSupportedException<T>(string? message, Exception? innerException)
=> throw new PlatformNotSupportedException(message, innerException);
/// <summary>
/// Throws a new <see cref="SynchronizationLockException"/>.
@@ -717,7 +754,8 @@ public static partial class ThrowHelper
/// <exception cref="SynchronizationLockException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowSynchronizationLockException<T>(string? message) => throw new SynchronizationLockException(message);
public static T ThrowSynchronizationLockException<T>(string? message)
=> throw new SynchronizationLockException(message);
/// <summary>
/// Throws a new <see cref="SynchronizationLockException"/>.
@@ -728,7 +766,8 @@ public static partial class ThrowHelper
/// <exception cref="SynchronizationLockException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowSynchronizationLockException<T>(string? message, Exception? innerException) => throw new SynchronizationLockException(message, innerException);
public static T ThrowSynchronizationLockException<T>(string? message, Exception? innerException)
=> throw new SynchronizationLockException(message, innerException);
/// <summary>
/// Throws a new <see cref="TimeoutException"/>.
@@ -758,7 +797,8 @@ public static partial class ThrowHelper
/// <exception cref="TimeoutException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowTimeoutException<T>(string? message, Exception? innerException) => throw new TimeoutException(message, innerException);
public static T ThrowTimeoutException<T>(string? message, Exception? innerException)
=> throw new TimeoutException(message, innerException);
/// <summary>
/// Throws a new <see cref="UnauthorizedAccessException"/>.
@@ -777,7 +817,8 @@ public static partial class ThrowHelper
/// <exception cref="UnauthorizedAccessException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowUnauthorizedAccessException<T>(string? message) => throw new UnauthorizedAccessException(message);
public static T ThrowUnauthorizedAccessException<T>(string? message)
=> throw new UnauthorizedAccessException(message);
/// <summary>
/// Throws a new <see cref="UnauthorizedAccessException"/>.
@@ -788,7 +829,8 @@ public static partial class ThrowHelper
/// <exception cref="UnauthorizedAccessException">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowUnauthorizedAccessException<T>(string? message, Exception? innerException) => throw new UnauthorizedAccessException(message, innerException);
public static T ThrowUnauthorizedAccessException<T>(string? message, Exception? innerException)
=> throw new UnauthorizedAccessException(message, innerException);
/// <summary>
/// Throws a new <see cref="Win32Exception"/>.
@@ -839,5 +881,6 @@ public static partial class ThrowHelper
/// <exception cref="Win32Exception">Thrown with the specified parameter.</exception>
/// <returns>This method always throws, so it actually never returns a value.</returns>
[DoesNotReturn]
public static T ThrowWin32Exception<T>(string? message, Exception? innerException) => throw new Win32Exception(message, innerException);
public static T ThrowWin32Exception<T>(string? message, Exception? innerException)
=> throw new Win32Exception(message, innerException);
}
+85 -42
View File
@@ -28,7 +28,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="ArrayTypeMismatchException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowArrayTypeMismatchException(string? message) => throw new ArrayTypeMismatchException(message);
public static void ThrowArrayTypeMismatchException(string? message)
=> throw new ArrayTypeMismatchException(message);
/// <summary>
/// Throws a new <see cref="ArrayTypeMismatchException"/>.
@@ -37,7 +38,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="ArrayTypeMismatchException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArrayTypeMismatchException(string? message, Exception? innerException) => throw new ArrayTypeMismatchException(message, innerException);
public static void ThrowArrayTypeMismatchException(string? message, Exception? innerException)
=> throw new ArrayTypeMismatchException(message, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentException"/>.
@@ -61,7 +63,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="ArgumentException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArgumentException(string? message, Exception? innerException) => throw new ArgumentException(message, innerException);
public static void ThrowArgumentException(string? message, Exception? innerException)
=> throw new ArgumentException(message, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentException"/>.
@@ -70,7 +73,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="ArgumentException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArgumentException(string? name, string? message) => throw new ArgumentException(message, name);
public static void ThrowArgumentException(string? name, string? message)
=> throw new ArgumentException(message, name);
/// <summary>
/// Throws a new <see cref="ArgumentException"/>.
@@ -80,7 +84,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="ArgumentException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArgumentException(string? name, string? message, Exception? innerException) => throw new ArgumentException(message, name, innerException);
public static void ThrowArgumentException(string? name, string? message, Exception? innerException)
=> throw new ArgumentException(message, name, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentNullException"/>.
@@ -104,7 +109,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="ArgumentNullException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArgumentNullException(string? name, Exception? innerException) => throw new ArgumentNullException(name, innerException);
public static void ThrowArgumentNullException(string? name, Exception? innerException)
=> throw new ArgumentNullException(name, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentNullException"/>.
@@ -113,7 +119,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="ArgumentNullException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArgumentNullException(string? name, string? message) => throw new ArgumentNullException(name, message);
public static void ThrowArgumentNullException(string? name, string? message)
=> throw new ArgumentNullException(name, message);
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/>.
@@ -137,7 +144,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeException(string? name, Exception? innerException) => throw new ArgumentOutOfRangeException(name, innerException);
public static void ThrowArgumentOutOfRangeException(string? name, Exception? innerException)
=> throw new ArgumentOutOfRangeException(name, innerException);
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/>.
@@ -146,7 +154,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeException(string? name, string? message) => throw new ArgumentOutOfRangeException(name, message);
public static void ThrowArgumentOutOfRangeException(string? name, string? message)
=> throw new ArgumentOutOfRangeException(name, message);
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/>.
@@ -156,7 +165,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowArgumentOutOfRangeException(string? name, object? value, string? message) => throw new ArgumentOutOfRangeException(name, value, message);
public static void ThrowArgumentOutOfRangeException(string? name, object? value, string? message)
=> throw new ArgumentOutOfRangeException(name, value, message);
/// <summary>
/// Throws a new <see cref="COMException"/>.
@@ -180,7 +190,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="COMException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowCOMException(string? message, Exception? innerException) => throw new COMException(message, innerException);
public static void ThrowCOMException(string? message, Exception? innerException)
=> throw new COMException(message, innerException);
/// <summary>
/// Throws a new <see cref="COMException"/>.
@@ -213,7 +224,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="ExternalException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowExternalException(string? message, Exception? innerException) => throw new ExternalException(message, innerException);
public static void ThrowExternalException(string? message, Exception? innerException)
=> throw new ExternalException(message, innerException);
/// <summary>
/// Throws a new <see cref="ExternalException"/>.
@@ -222,7 +234,8 @@ public static partial class ThrowHelper
/// <param name="error">The HRESULT of the error to include.</param>
/// <exception cref="ExternalException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowExternalException(string? message, int error) => throw new ExternalException(message, error);
public static void ThrowExternalException(string? message, int error)
=> throw new ExternalException(message, error);
/// <summary>
/// Throws a new <see cref="FormatException"/>.
@@ -246,7 +259,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="FormatException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowFormatException(string? message, Exception? innerException) => throw new FormatException(message, innerException);
public static void ThrowFormatException(string? message, Exception? innerException)
=> throw new FormatException(message, innerException);
/// <summary>
/// Throws a new <see cref="InsufficientMemoryException"/>.
@@ -261,7 +275,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="InsufficientMemoryException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowInsufficientMemoryException(string? message) => throw new InsufficientMemoryException(message);
public static void ThrowInsufficientMemoryException(string? message)
=> throw new InsufficientMemoryException(message);
/// <summary>
/// Throws a new <see cref="InsufficientMemoryException"/>.
@@ -270,7 +285,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="InsufficientMemoryException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowInsufficientMemoryException(string? message, Exception? innerException) => throw new InsufficientMemoryException(message, innerException);
public static void ThrowInsufficientMemoryException(string? message, Exception? innerException)
=> throw new InsufficientMemoryException(message, innerException);
/// <summary>
/// Throws a new <see cref="InvalidDataException"/>.
@@ -294,7 +310,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="InvalidDataException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowInvalidDataException(string? message, Exception? innerException) => throw new InvalidDataException(message, innerException);
public static void ThrowInvalidDataException(string? message, Exception? innerException)
=> throw new InvalidDataException(message, innerException);
/// <summary>
/// Throws a new <see cref="InvalidOperationException"/>.
@@ -318,7 +335,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="InvalidOperationException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowInvalidOperationException(string? message, Exception? innerException) => throw new InvalidOperationException(message, innerException);
public static void ThrowInvalidOperationException(string? message, Exception? innerException)
=> throw new InvalidOperationException(message, innerException);
/// <summary>
/// Throws a new <see cref="LockRecursionException"/>.
@@ -342,7 +360,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="LockRecursionException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowLockRecursionException(string? message, Exception? innerException) => throw new LockRecursionException(message, innerException);
public static void ThrowLockRecursionException(string? message, Exception? innerException)
=> throw new LockRecursionException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingFieldException"/>.
@@ -366,7 +385,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="MissingFieldException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowMissingFieldException(string? message, Exception? innerException) => throw new MissingFieldException(message, innerException);
public static void ThrowMissingFieldException(string? message, Exception? innerException)
=> throw new MissingFieldException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingFieldException"/>.
@@ -375,7 +395,8 @@ public static partial class ThrowHelper
/// <param name="fieldName">The target field being retrieved.</param>
/// <exception cref="MissingFieldException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowMissingFieldException(string? className, string? fieldName) => throw new MissingFieldException(className, fieldName);
public static void ThrowMissingFieldException(string? className, string? fieldName)
=> throw new MissingFieldException(className, fieldName);
/// <summary>
/// Throws a new <see cref="MissingMemberException"/>.
@@ -399,7 +420,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="MissingMemberException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowMissingMemberException(string? message, Exception? innerException) => throw new MissingMemberException(message, innerException);
public static void ThrowMissingMemberException(string? message, Exception? innerException)
=> throw new MissingMemberException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingMemberException"/>.
@@ -408,7 +430,8 @@ public static partial class ThrowHelper
/// <param name="memberName">The target member being retrieved.</param>
/// <exception cref="MissingMemberException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowMissingMemberException(string? className, string? memberName) => throw new MissingMemberException(className, memberName);
public static void ThrowMissingMemberException(string? className, string? memberName)
=> throw new MissingMemberException(className, memberName);
/// <summary>
/// Throws a new <see cref="MissingMethodException"/>.
@@ -432,7 +455,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="MissingMethodException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowMissingMethodException(string? message, Exception? innerException) => throw new MissingMethodException(message, innerException);
public static void ThrowMissingMethodException(string? message, Exception? innerException)
=> throw new MissingMethodException(message, innerException);
/// <summary>
/// Throws a new <see cref="MissingMethodException"/>.
@@ -441,7 +465,8 @@ public static partial class ThrowHelper
/// <param name="methodName">The target method being retrieved.</param>
/// <exception cref="MissingMethodException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowMissingMethodException(string? className, string? methodName) => throw new MissingMethodException(className, methodName);
public static void ThrowMissingMethodException(string? className, string? methodName)
=> throw new MissingMethodException(className, methodName);
/// <summary>
/// Throws a new <see cref="NotSupportedException"/>.
@@ -465,7 +490,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="NotSupportedException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowNotSupportedException(string? message, Exception? innerException) => throw new NotSupportedException(message, innerException);
public static void ThrowNotSupportedException(string? message, Exception? innerException)
=> throw new NotSupportedException(message, innerException);
/// <summary>
/// Throws a new <see cref="ObjectDisposedException"/>.
@@ -473,7 +499,8 @@ public static partial class ThrowHelper
/// <param name="objectName">The name of the disposed object.</param>
/// <exception cref="ObjectDisposedException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowObjectDisposedException(string? objectName) => throw new ObjectDisposedException(objectName);
public static void ThrowObjectDisposedException(string? objectName)
=> throw new ObjectDisposedException(objectName);
/// <summary>
/// Throws a new <see cref="ObjectDisposedException"/>.
@@ -482,7 +509,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="ObjectDisposedException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowObjectDisposedException(string? objectName, Exception? innerException) => throw new ObjectDisposedException(objectName, innerException);
public static void ThrowObjectDisposedException(string? objectName, Exception? innerException)
=> throw new ObjectDisposedException(objectName, innerException);
/// <summary>
/// Throws a new <see cref="ObjectDisposedException"/>.
@@ -491,7 +519,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="ObjectDisposedException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowObjectDisposedException(string? objectName, string? message) => throw new ObjectDisposedException(objectName, message);
public static void ThrowObjectDisposedException(string? objectName, string? message)
=> throw new ObjectDisposedException(objectName, message);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -506,7 +535,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="OperationCanceledException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowOperationCanceledException(string? message) => throw new OperationCanceledException(message);
public static void ThrowOperationCanceledException(string? message)
=> throw new OperationCanceledException(message);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -515,7 +545,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="OperationCanceledException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowOperationCanceledException(string? message, Exception? innerException) => throw new OperationCanceledException(message, innerException);
public static void ThrowOperationCanceledException(string? message, Exception? innerException)
=> throw new OperationCanceledException(message, innerException);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -523,7 +554,8 @@ public static partial class ThrowHelper
/// <param name="token">The <see cref="CancellationToken"/> in use.</param>
/// <exception cref="OperationCanceledException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowOperationCanceledException(CancellationToken token) => throw new OperationCanceledException(token);
public static void ThrowOperationCanceledException(CancellationToken token)
=> throw new OperationCanceledException(token);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -532,7 +564,8 @@ public static partial class ThrowHelper
/// <param name="token">The <see cref="CancellationToken"/> in use.</param>
/// <exception cref="OperationCanceledException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowOperationCanceledException(string? message, CancellationToken token) => throw new OperationCanceledException(message, token);
public static void ThrowOperationCanceledException(string? message, CancellationToken token)
=> throw new OperationCanceledException(message, token);
/// <summary>
/// Throws a new <see cref="OperationCanceledException"/>.
@@ -542,7 +575,9 @@ public static partial class ThrowHelper
/// <param name="token">The <see cref="CancellationToken"/> in use.</param>
/// <exception cref="OperationCanceledException">Thrown with the specified parameters.</exception>
[DoesNotReturn]
public static void ThrowOperationCanceledException(string? message, Exception? innerException, CancellationToken token) => throw new OperationCanceledException(message, innerException, token);
public static void ThrowOperationCanceledException(string? message, Exception? innerException,
CancellationToken token)
=> throw new OperationCanceledException(message, innerException, token);
/// <summary>
/// Throws a new <see cref="PlatformNotSupportedException"/>.
@@ -557,7 +592,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="PlatformNotSupportedException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowPlatformNotSupportedException(string? message) => throw new PlatformNotSupportedException(message);
public static void ThrowPlatformNotSupportedException(string? message)
=> throw new PlatformNotSupportedException(message);
/// <summary>
/// Throws a new <see cref="PlatformNotSupportedException"/>.
@@ -566,7 +602,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="PlatformNotSupportedException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowPlatformNotSupportedException(string? message, Exception? innerException) => throw new PlatformNotSupportedException(message, innerException);
public static void ThrowPlatformNotSupportedException(string? message, Exception? innerException)
=> throw new PlatformNotSupportedException(message, innerException);
/// <summary>
/// Throws a new <see cref="SynchronizationLockException"/>.
@@ -581,7 +618,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="SynchronizationLockException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowSynchronizationLockException(string? message) => throw new SynchronizationLockException(message);
public static void ThrowSynchronizationLockException(string? message)
=> throw new SynchronizationLockException(message);
/// <summary>
/// Throws a new <see cref="SynchronizationLockException"/>.
@@ -590,7 +628,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="SynchronizationLockException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowSynchronizationLockException(string? message, Exception? innerException) => throw new SynchronizationLockException(message, innerException);
public static void ThrowSynchronizationLockException(string? message, Exception? innerException)
=> throw new SynchronizationLockException(message, innerException);
/// <summary>
/// Throws a new <see cref="TimeoutException"/>.
@@ -614,7 +653,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="TimeoutException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowTimeoutException(string? message, Exception? innerException) => throw new TimeoutException(message, innerException);
public static void ThrowTimeoutException(string? message, Exception? innerException)
=> throw new TimeoutException(message, innerException);
/// <summary>
/// Throws a new <see cref="UnauthorizedAccessException"/>.
@@ -629,7 +669,8 @@ public static partial class ThrowHelper
/// <param name="message">The message to include in the exception.</param>
/// <exception cref="UnauthorizedAccessException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowUnauthorizedAccessException(string? message) => throw new UnauthorizedAccessException(message);
public static void ThrowUnauthorizedAccessException(string? message)
=> throw new UnauthorizedAccessException(message);
/// <summary>
/// Throws a new <see cref="UnauthorizedAccessException"/>.
@@ -638,7 +679,8 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="UnauthorizedAccessException">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowUnauthorizedAccessException(string? message, Exception? innerException) => throw new UnauthorizedAccessException(message, innerException);
public static void ThrowUnauthorizedAccessException(string? message, Exception? innerException)
=> throw new UnauthorizedAccessException(message, innerException);
/// <summary>
/// Throws a new <see cref="Win32Exception"/>.
@@ -679,5 +721,6 @@ public static partial class ThrowHelper
/// <param name="innerException">The inner <see cref="Exception"/> to include.</param>
/// <exception cref="Win32Exception">Thrown with the specified parameter.</exception>
[DoesNotReturn]
public static void ThrowWin32Exception(string? message, Exception? innerException) => throw new Win32Exception(message, innerException);
public static void ThrowWin32Exception(string? message, Exception? innerException)
=> throw new Win32Exception(message, innerException);
}
+4 -3
View File
@@ -12,16 +12,17 @@ namespace FisheryLib;
public static class Equality
{
/// <summary>
/// Generic overload of Equals to avoid boxing. Makes use of function pointers for best performance and does not throw on null for reference types.
/// Generic overload of Equals to avoid boxing. Makes use of function pointers for best performance and does not
/// throw on null for reference types.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool Equals<T>([AllowNull] this T @this, [AllowNull] T other)
=> FunctionPointers.Equals<T>.Default(@this!, other!);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool Equals<T>([AllowNull] this ref T @this, [AllowNull] ref T other)
public static unsafe bool Equals<T>(this ref T @this, ref T other)
where T : struct // required by the compiler for 'this ref'
=> FunctionPointers.EqualsByRef<T>.Default(ref @this!, ref other!);
=> FunctionPointers.EqualsByRef<T>.Default(ref @this, ref other);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool EqualsByRef<T>([AllowNull] ref T @this, [AllowNull] ref T other)
+1
View File
@@ -336,3 +336,4 @@ public abstract class ClassWithFishPatches : IHasFishPatch
public virtual bool RequiresLoadedGameForPatching => false;
private FishPatchHolder? _patchHolder;
}*/
File diff suppressed because it is too large Load Diff
+14 -1
View File
@@ -3,6 +3,8 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
//#define DEBUG
global using System;
global using System.Collections.Generic;
global using System.Reflection;
@@ -11,12 +13,23 @@ global using FisheryLib.Utility.Diagnostics;
global using CodeInstructions = System.Collections.Generic.IEnumerable<HarmonyLib.CodeInstruction>;
using System.Security;
using System.Diagnostics;
using System.Runtime.CompilerServices;
[assembly: AllowPartiallyTrustedCallers]
[assembly: SecurityTransparent]
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]
[assembly: Debuggable(false, false)]
[module: SkipLocalsInit]
namespace FisheryLib;
public static class FisheryLib
{
public const decimal VERSION = 0.380085M;
public static decimal CurrentlyLoadedVersion { get; } = VERSION;
}
/*namespace FisheryLib;
public class Fishery : Harmony
{
+71 -62
View File
@@ -1,65 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RootNamespace>FisheryLib</RootNamespace>
<AssemblyName>1Fishery</AssemblyName>
<TargetFramework>net48</TargetFramework>
<LangVersion>preview</LangVersion>
<PlatformTarget>AnyCPU</PlatformTarget>
<Optimize>true</Optimize>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Authors>bradson</Authors>
<Copyright>Copyright (c) 2022 bradson</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<AnalysisLevel>preview-all</AnalysisLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>5</WarningLevel>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Configurations>v1.3;v1.4</Configurations>
</PropertyGroup>
<Choose>
<When Condition="'$(Configuration)' == 'v1.3'">
<PropertyGroup>
<OutputPath>..\..\1.3\Assemblies\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.*" />
</ItemGroup>
</When>
<When Condition="'$(Configuration)' == 'v1.4'">
<PropertyGroup>
<OutputPath>..\..\1.4\Assemblies\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*-*" />
</ItemGroup>
</When>
</Choose>
<ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.*" ExcludeAssets="runtime" />
<PackageReference Include="Nullable" Version="1.*" />
<PackageReference Include="IndexRange" Version="1.*" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" />
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
<Publicize Include="Assembly-CSharp;0Harmony" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeCast" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<PropertyGroup>
<RootNamespace>FisheryLib</RootNamespace>
<AssemblyName>1Fishery</AssemblyName>
<TargetFramework>net48</TargetFramework>
<LangVersion>preview</LangVersion>
<PlatformTarget>AnyCPU</PlatformTarget>
<Optimize>true</Optimize>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Authors>bradson</Authors>
<Copyright>Copyright (c) 2022 bradson</Copyright>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<AnalysisLevel>preview-all</AnalysisLevel>
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>5</WarningLevel>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<Configurations>v1.4;v1.3</Configurations>
<Version>0.3.800.85</Version>
<NoWarn>1701;1702;IL2121;CS8500;CS9082</NoWarn>
</PropertyGroup>
<Choose>
<When Condition="'$(Configuration)' == 'v1.4'">
<PropertyGroup>
<OutputPath>..\..\1.4\Assemblies\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.*" />
</ItemGroup>
</When>
<When Condition="'$(Configuration)' == 'v1.3'">
<PropertyGroup>
<OutputPath>..\..\1.3\Assemblies\</OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.*" />
</ItemGroup>
</When>
</Choose>
<ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.*" ExcludeAssets="runtime" />
<PackageReference Include="PolySharp" Version="1.*" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.*" />
<PackageReference Include="Krafs.Publicizer" Version="2.*" />
<Publicize Include="Assembly-CSharp;0Harmony" IncludeVirtualMembers="false" />
<DoNotPublicize Include="0Harmony:System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute" />
<DoNotPublicize Include="0Harmony:Mono.Collections.Generic.Collection`1" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.buckets" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.entries" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2.version" />
<Publicize Include="mscorlib:System.Collections.Generic.Dictionary`2+Entry" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._items" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._version" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1._size" />
<Publicize Include="mscorlib:System.Collections.Generic.List`1.EnsureCapacity" />
<Publicize Include="System.Core:System.Collections.Generic.HashSet`1._version" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCast" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeEnumCastLong" />
<Publicize Include="mscorlib:System.Runtime.CompilerServices.JitHelpers.UnsafeCast" />
<Publicize Include="mscorlib:System.Reflection.Emit.Label.label" />
<Publicize Include="mscorlib:System.String.m_firstChar" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath />
</None>
</ItemGroup>
</Project>
@@ -208,3 +208,4 @@ public enum FisheryPatchType
Finalizer,
ReversePatch
}*/
+199 -52
View File
@@ -3,16 +3,25 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using FisheryLib.Collections;
namespace FisheryLib.FunctionPointers;
public static class Convert<TFrom> where TFrom : IConvertible
public static class Convert<TFrom>
{
public static class To<TTo>
{
public static readonly unsafe delegate*<TFrom, IFormatProvider, TTo> Default = Convert.GetDefaultConverter<TFrom, TTo>();
public static readonly unsafe delegate*<TFrom, IFormatProvider, TTo> Default
= Convert.GetDefaultConverter<TFrom, TTo>();
}
/// <summary>
/// Used when TFrom isn't a known sealed type, i.e. with just the IConvertible interface
/// Used when TFrom isn't a known sealed type, e.g. with just the IConvertible interface
/// </summary>
internal static class Fallback<TTo>
{
@@ -23,64 +32,202 @@ public static class Convert<TFrom> where TFrom : IConvertible
internal static class Convert
{
private static MethodInfo GetConverterMethod<TFrom>(Type to) where TFrom : IConvertible
private static class FromObjectTo<TTo>
{
var info = typeof(TFrom).GetMethod(GetConvertMethodName(to));
if (info.GetParameters().Length > 1)
private static FishTable<RuntimeTypeHandle, FuncWrapper> _delegates = new();
static FromObjectTo()
=> _delegates.EntryAdded += static pair =>
{
var from = Type.GetTypeFromHandle(pair.Key);
ref var entryRef = ref _delegates.GetReference(pair.Key);
var to = typeof(TTo);
entryRef.Func = CreateObjectConverter(from, to);
};
private static Func<object, IFormatProvider, TTo> CreateObjectConverter(Type from, Type to)
{
info = typeof(Convert)
.GetMethod(nameof(ConvertToType), AccessTools.allDeclared)
.MakeGenericMethod(new[] { typeof(TFrom), to });
try
{
var dm = new DynamicMethod($"ObjectConverter_{from}_{to}", to,
new[] { typeof(object), typeof(IFormatProvider) }, typeof(FromObjectTo<TTo>), true);
var il = dm.GetILGenerator();
il.Emit(FishTranspiler.This);
if (from.IsValueType)
{
il.Emit(FishTranspiler.Call(typeof(FromObjectTo<TTo>), nameof(UnboxSafely),
generics: new[] { from }));
}
il.Emit(FishTranspiler.Argument(1));
il.Emit(FishTranspiler.Call(GetConverterMethod(from, to)));
il.Emit(FishTranspiler.Return);
return (Func<object, IFormatProvider, TTo>)dm.CreateDelegate(typeof(Func<object, IFormatProvider, TTo>));
}
catch (Exception ex)
{
throw new($"Failed to compile object converter from {from.FullDescription()} to {
to.FullDescription()}\n{ex}");
}
}
return info;
public static TTo Method(object obj, IFormatProvider provider)
=> _delegates.GetOrAdd(obj.GetType().TypeHandle).Func(obj, provider);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SecuritySafeCritical]
private static ref T UnboxSafely<T>(object obj) where T : struct
=> ref Unsafe.Unbox<T>(obj is T ? obj : default);
private record struct FuncWrapper
{
public Func<object, IFormatProvider, TTo> Func;
}
}
internal static Func<TFrom, IFormatProvider, TTo> GetDefaultConverterFunc<TFrom, TTo>() where TFrom : IConvertible
=> (Func<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
.CreateDelegate(typeof(Func<TFrom, IFormatProvider, TTo>));
private static MethodInfo GetConverterMethod<TFrom>(Type to) => GetConverterMethod(typeof(TFrom), to);
internal static unsafe delegate*<TFrom, IFormatProvider, TTo> GetDefaultConverter<TFrom, TTo>() where TFrom : IConvertible
=> !typeof(TFrom).IsValueType && (!typeof(TFrom).IsSealed || typeof(TFrom).IsInterface || typeof(TFrom).IsAbstract)
? &Convert<TFrom>.Fallback<TTo>.FallbackMethod
: (delegate*<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
.MethodHandle.GetFunctionPointer();
private static MethodInfo GetConverterMethod(Type from, Type to)
{
try
{
var info = _convertMethodsByType.TryGetValue(to, out var value)
&& TryFindInterfaceMethod(from, value) is { } methodByName
? methodByName
: TryFindMethodThroughReflection(to, from);
if (info is null)
ThrowFailedToFindConverterInvalidOperationException(from, to);
if (info.IsStatic)
{
if (info.GetParameters() is [_])
info = GenerateWrapperMethod(from, to, info);
}
else if (info.GetParameters().Length > 1)
{
info = typeof(Convert)
.GetMethod(nameof(ConvertToType), AccessTools.allDeclared)!
.MakeGenericMethod(from, to);
}
return info;
}
catch (Exception e)
{
Log.Error($"Exception thrown while trying to get converter method:\n{e}");
throw;
}
}
private static MethodInfo? TryFindInterfaceMethod(Type from, MethodInfo interfaceMethod)
{
if (!typeof(IConvertible).IsAssignableFrom(from))
return null;
var interfaceMap = from.GetInterfaceMap(typeof(IConvertible));
var index = Array.IndexOf(interfaceMap.InterfaceMethods, interfaceMethod);
return index < 0 ? null : interfaceMap.TargetMethods[index];
}
[DoesNotReturn]
private static MethodInfo ThrowFailedToFindConverterInvalidOperationException(Type from, Type to)
=> ThrowHelper.ThrowInvalidOperationException<MethodInfo>($"Failed to find converter from type '{
from.FullDescription()}' to type '{to.FullDescription()}'");
private static MethodInfo? TryFindMethodThroughReflection(Type to, Type from)
{
var parameters = new[] { from };
var fromMethods = from.GetMethods(BindingFlags.Public | BindingFlags.Static);
if (TryFindMethod(fromMethods, "op_Implicit", parameters, to) is { } info)
return info;
var toMethods = to.GetMethods(BindingFlags.Public | BindingFlags.Static);
return TryFindMethod(toMethods, "op_Implicit", parameters, from)
?? TryFindMethod(fromMethods, "op_Explicit", parameters, to)
?? TryFindMethod(toMethods, "op_Explicit", parameters, from)
?? (!from.IsValueType
? typeof(FromObjectTo<>).MakeGenericType(to).GetMethod(nameof(FromObjectTo<object>.Method))
: ThrowFailedToFindConverterInvalidOperationException(from, to));
}
private static MethodInfo GenerateWrapperMethod(Type from, Type to, MethodBase method)
{
var dm = new DynamicMethod($"Wrapper_op_{from.Name}_{to.Name}", to,
new[] { from.IsValueType ? from.MakeByRefType() : from, typeof(IFormatProvider) }, from, true);
var il = dm.GetILGenerator();
il.Emit(FishTranspiler.Argument(0));
if (from.IsValueType)
il.Emit(FishTranspiler.LoadIndirectly(from));
il.Emit(FishTranspiler.Call(method));
il.Emit(FishTranspiler.Return);
return dm.CreateDelegate(from.IsValueType
? typeof(RefConverterFunc<,>).MakeGenericType(from, to)
: typeof(Func<,,>).MakeGenericType(from, typeof(IFormatProvider), to)).Method;
}
private static MethodInfo? TryFindMethod(MethodInfo[] array, string name, Type[] parameters, Type returnType)
{
for (var i = array.Length; i-- > 0;)
{
var method = array[i];
if (method.Name != name || method.ReturnType != returnType)
continue;
var testedParameters = method.GetParameters();
if (testedParameters.Length != parameters.Length)
continue;
var parametersMatch = true;
for (var j = testedParameters.Length; j-- > 0;)
{
if (testedParameters[j].ParameterType == parameters[j])
continue;
parametersMatch = false;
break;
}
if (parametersMatch)
return method;
}
return null;
}
internal static Func<TFrom, IFormatProvider, TTo> GetDefaultConverterFunc<TFrom, TTo>()
=> (Func<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
.CreateDelegate(typeof(Func<TFrom, IFormatProvider, TTo>));
internal static unsafe delegate*<TFrom, IFormatProvider, TTo> GetDefaultConverter<TFrom, TTo>()
=> !typeof(TFrom).IsValueType
&& (!typeof(TFrom).IsSealed || typeof(TFrom).IsInterface || typeof(TFrom).IsAbstract)
? &Convert<TFrom>.Fallback<TTo>.FallbackMethod
: (delegate*<TFrom, IFormatProvider, TTo>)GetConverterMethod<TFrom>(typeof(TTo))
.MethodHandle.GetFunctionPointer();
private static TTo ConvertToType<TFrom, TTo>(TFrom from, IFormatProvider provider) where TFrom : IConvertible
=> (TTo)from.ToType(typeof(TTo), provider);
private static string GetConvertMethodName(Type type)
=> type == typeof(bool)
? nameof(IConvertible.ToBoolean)
: type == typeof(char)
? nameof(IConvertible.ToChar)
: type == typeof(sbyte)
? nameof(IConvertible.ToSByte)
: type == typeof(byte)
? nameof(IConvertible.ToByte)
: type == typeof(short)
? nameof(IConvertible.ToInt16)
: type == typeof(ushort)
? nameof(IConvertible.ToUInt16)
: type == typeof(int)
? nameof(IConvertible.ToInt32)
: type == typeof(uint)
? nameof(IConvertible.ToUInt32)
: type == typeof(long)
? nameof(IConvertible.ToInt64)
: type == typeof(ulong)
? nameof(IConvertible.ToUInt64)
: type == typeof(float)
? nameof(IConvertible.ToSingle)
: type == typeof(double)
? nameof(IConvertible.ToDouble)
: type == typeof(decimal)
? nameof(IConvertible.ToDecimal)
: type == typeof(DateTime)
? nameof(IConvertible.ToDateTime)
: type == typeof(string)
? nameof(IConvertible.ToString)
//: type == typeof(object)
//? value
: nameof(IConvertible.ToType);
private static readonly FishTable<Type, MethodInfo> _convertMethodsByType
= typeof(IConvertible).GetMethods()
.Where(static method
=> method.GetParameters() is [{ ParameterType: var type }] && type == typeof(IFormatProvider))
.ToFishTable(static info => info.ReturnType);
}
[SuppressMessage("ReSharper", "TypeParameterCanBeVariant")]
file delegate TReturn RefConverterFunc<TInstance, TReturn>(TInstance instance, IFormatProvider provider);
+54 -77
View File
@@ -3,118 +3,95 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
public static class Equals<T>
{
public static readonly unsafe delegate*<T, T, bool> Default = (delegate*<T, T, bool>)FunctionPointers.Equals.MakeDelegate(typeof(T));
public static readonly unsafe delegate*<T, T, bool> Default
= (delegate*<T, T, bool>)FunctionPointers.Equals.EqualsMethods.GetFunctionPointer(typeof(T));
}
public static class Equals
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public static class Methods
{
public static bool Byte(byte x, byte y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Byte(byte x, byte y) => x == y;
public static bool Int(int x, int y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Int(int x, int y) => x == y;
public static bool Uint(uint x, uint y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Uint(uint x, uint y) => x == y;
public static bool Ushort(ushort x, ushort y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Ushort(ushort x, ushort y) => x == y;
public static bool IntPtr(IntPtr x, IntPtr y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IntPtr(IntPtr x, IntPtr y) => x == y;
public static bool String(string x, string y)
=> string.Equals(x, y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool String(string x, string y) => string.Equals(x, y);
public static unsafe bool VoidPtr(void* x, void* y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool VoidPtr(void* x, void* y) => x == y;
public static bool RuntimeFieldHandle(RuntimeFieldHandle x, RuntimeFieldHandle y)
=> x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SecuritySafeCritical]
public static bool RuntimeFieldHandle(RuntimeFieldHandle x, RuntimeFieldHandle y) => x.Value == y.Value;
public static bool RuntimeTypeHandle(RuntimeTypeHandle x, RuntimeTypeHandle y)
=> x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SecuritySafeCritical]
public static bool RuntimeTypeHandle(RuntimeTypeHandle x, RuntimeTypeHandle y) => x.Value == y.Value;
public static bool RuntimeMethodHandle(RuntimeMethodHandle x, RuntimeMethodHandle y)
=> x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SecuritySafeCritical]
public static bool RuntimeMethodHandle(RuntimeMethodHandle x, RuntimeMethodHandle y) => x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Label(Label x, Label y) => x.label == y.label;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Enum<T>(T x, T y) where T : struct
=> JitHelpers.UnsafeEnumCast(x) == JitHelpers.UnsafeEnumCast(y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool LongEnum<T>(T x, T y) where T : struct
=> JitHelpers.UnsafeEnumCastLong(x) == JitHelpers.UnsafeEnumCastLong(y);
public static bool EquatableValueType<T>(T x, T y) where T : struct, IEquatable<T>
=> x.Equals(y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EquatableValueType<T>(T x, T y) where T : struct, IEquatable<T> => x.Equals(y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Nullable<T>(T? x, T? y) where T : struct, IEquatable<T>
=> x.HasValue
? y.HasValue && x.Value.Equals(y.Value)
: !y.HasValue;
? y.HasValue && x.Value.Equals(y.Value)
: !y.HasValue;
public static bool EquatableReferenceType<T>(T x, T y) where T : IEquatable<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EquatableReferenceType<T>(T? x, T? y) where T : class, IEquatable<T>
=> x != null
? y != null && x.Equals(y)
: y == null;
? y != null && x.Equals(y)
: y == null;
public static bool Reference<T>(T x, T y) where T : class
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Reference<T>(T x, T y) where T : class => x == y;
public static bool Object<T>(T x, T y)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Object<T>(T? x, T? y) where T : class
=> x != null
? y != null && x.Equals(y)
: y == null;
? y != null && x.Equals(y)
: y == null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool ValueType<T>(T x, T y) where T : struct => x.Equals(y);
}
internal static unsafe IntPtr MakeDelegate(Type type)
=> _equalsMethods.Where(m => !m.IsGenericMethod)
.FirstOrDefault(m
=> m.GetParameters().First().ParameterType == type)
is { } specializedMethod
? specializedMethod.GetFunctionPointer()
: type.IsAssignableTo(typeof(IEquatable<>), type)
? GetMethod(
type.IsValueType
? nameof(Methods.EquatableValueType)
: nameof(Methods.EquatableReferenceType))
.MakeGenericMethod(type).GetFunctionPointer()
: type.IsNullable(out var genericArgument)
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
: GetMethod(
type.IsEnum
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
switch
{
TypeCode.Int16
or TypeCode.SByte
or TypeCode.Byte
or TypeCode.UInt16
or TypeCode.Int32
or TypeCode.UInt32 => nameof(Methods.Enum),
TypeCode.Int64
or TypeCode.UInt64 => nameof(Methods.LongEnum),
_ => ThrowHelper.ThrowNotSupportedException<string>()
}
: nameof(Methods.Object)
).MakeGenericMethod(type).GetFunctionPointer();
private static MethodInfo GetMethod(string name)
=> Array.Find(_equalsMethods, m => m.Name == name);
private static MethodInfo[] _equalsMethods
= typeof(Methods)
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
.Where(m
=> m.ReturnType == typeof(bool)
&& m.GetParameters().Length == 2)
.ToArray();
public static readonly MethodFactory.Equals EqualsMethods = new(typeof(Methods), "FisheryEqualsMethod_", false);
}
@@ -3,118 +3,96 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
public static class EqualsByRef<T>
{
public static readonly unsafe delegate*<ref T, ref T, bool> Default = (delegate*<ref T, ref T, bool>)EqualsByRef.MakeDelegate(typeof(T));
public static readonly unsafe delegate*<ref T, ref T, bool> Default
= (delegate*<ref T, ref T, bool>)EqualsByRef.EqualsMethods.GetFunctionPointer(typeof(T));
}
public static class EqualsByRef
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public static class Methods
{
public static bool Byte(ref byte x, ref byte y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Byte(ref byte x, ref byte y) => x == y;
public static bool Int(ref int x, ref int y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Int(ref int x, ref int y) => x == y;
public static bool Uint(ref uint x, ref uint y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Uint(ref uint x, ref uint y) => x == y;
public static bool Ushort(ref ushort x, ref ushort y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Ushort(ref ushort x, ref ushort y) => x == y;
public static bool IntPtr(ref IntPtr x, ref IntPtr y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IntPtr(ref IntPtr x, ref IntPtr y) => x == y;
public static bool String(ref string x, ref string y)
=> string.Equals(x, y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool String(ref string x, ref string y) => string.Equals(x, y);
public static unsafe bool VoidPtr(ref void* x, ref void* y)
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool VoidPtr(ref void* x, ref void* y) => x == y;
public static bool RuntimeFieldHandle(ref RuntimeFieldHandle x, ref RuntimeFieldHandle y)
=> x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SecuritySafeCritical]
public static bool RuntimeFieldHandle(ref RuntimeFieldHandle x, ref RuntimeFieldHandle y) => x.Value == y.Value;
public static bool RuntimeTypeHandle(ref RuntimeTypeHandle x, ref RuntimeTypeHandle y)
=> x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SecuritySafeCritical]
public static bool RuntimeTypeHandle(ref RuntimeTypeHandle x, ref RuntimeTypeHandle y) => x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SecuritySafeCritical]
public static bool RuntimeMethodHandle(ref RuntimeMethodHandle x, ref RuntimeMethodHandle y)
=> x.Value == y.Value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Label(ref Label x, ref Label y) => x.label == y.label;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Enum<T>(ref T x, ref T y) where T : struct
=> JitHelpers.UnsafeEnumCast(x) == JitHelpers.UnsafeEnumCast(y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool LongEnum<T>(ref T x, ref T y) where T : struct
=> JitHelpers.UnsafeEnumCastLong(x) == JitHelpers.UnsafeEnumCastLong(y);
public static bool EquatableValueType<T>(ref T x, ref T y) where T : struct, IEquatable<T>
=> x.Equals(y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EquatableValueType<T>(ref T x, ref T y) where T : struct, IEquatable<T> => x.Equals(y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Nullable<T>(ref T? x, ref T? y) where T : struct, IEquatable<T>
=> x.HasValue
? y.HasValue && x.Value.Equals(y.Value)
: !y.HasValue;
? y.HasValue && x.Value.Equals(y.Value)
: !y.HasValue;
public static bool EquatableReferenceType<T>(ref T x, ref T y) where T : IEquatable<T>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool EquatableReferenceType<T>(ref T? x, ref T? y) where T : class, IEquatable<T>
=> x != null
? y != null && x.Equals(y)
: y == null;
? y != null && x.Equals(y)
: y == null;
public static bool Reference<T>(ref T x, ref T y) where T : class
=> x == y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Reference<T>(ref T x, ref T y) where T : class => x == y;
public static bool Object<T>(ref T x, ref T y)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Object<T>(ref T? x, ref T? y) where T : class
=> x != null
? y != null && x.Equals(y)
: y == null;
? y != null && x.Equals(y)
: y == null;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool ValueType<T>(ref T x, ref T y) where T : struct => x.Equals(y);
}
internal static unsafe IntPtr MakeDelegate(Type type)
=> _equalsMethods.Where(m => !m.IsGenericMethod)
.FirstOrDefault(m
=> m.GetParameters().First().ParameterType.GetElementType() == type)
is { } specializedMethod
? specializedMethod.GetFunctionPointer()
: type.IsAssignableTo(typeof(IEquatable<>), type)
? GetMethod(
type.IsValueType
? nameof(Methods.EquatableValueType)
: nameof(Methods.EquatableReferenceType))
.MakeGenericMethod(type).GetFunctionPointer()
: type.IsNullable(out var genericArgument)
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
: GetMethod(
type.IsEnum
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
switch
{
TypeCode.Int16
or TypeCode.SByte
or TypeCode.Byte
or TypeCode.UInt16
or TypeCode.Int32
or TypeCode.UInt32 => nameof(Methods.Enum),
TypeCode.Int64
or TypeCode.UInt64 => nameof(Methods.LongEnum),
_ => ThrowHelper.ThrowNotSupportedException<string>()
}
: nameof(Methods.Object)
).MakeGenericMethod(type).GetFunctionPointer();
private static MethodInfo GetMethod(string name)
=> Array.Find(_equalsMethods, m => m.Name == name);
private static MethodInfo[] _equalsMethods
= typeof(Methods)
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
.Where(m
=> m.ReturnType == typeof(bool)
&& m.GetParameters().Length == 2)
.ToArray();
public static readonly MethodFactory.Equals EqualsMethods = new(typeof(Methods), "FisheryEqualsByRefMethod_", true);
}
@@ -3,104 +3,50 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
public static class GetHashCode<T>
{
public static readonly unsafe delegate*<T, int> Default = (delegate*<T, int>)FunctionPointers.GetHashCode.MakeDelegate(typeof(T));
public static readonly unsafe delegate*<T, int> Default
= (delegate*<T, int>)FunctionPointers.GetHashCode.HashCodeMethods.GetFunctionPointer(typeof(T));
}
public static class GetHashCode
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public static class Methods
{
public static int Byte(byte obj)
=> obj;
public static int Byte(byte obj) => obj;
public static int Int(int obj)
=> obj;
public static int Int(int obj) => obj;
public static int Uint(uint obj)
=> (int)obj;
public static int Uint(uint obj) => (int)obj;
public static int Ushort(ushort obj)
=> obj;
public static int Ushort(ushort obj) => obj;
public static unsafe int VoidPtr(void* obj)
=> (int)obj;
public static unsafe int VoidPtr(void* obj) => (int)obj;
public static int Enum<T>(T obj) where T : struct
=> JitHelpers.UnsafeEnumCast(obj).GetHashCode();
public static int Enum<T>(T obj) where T : struct => JitHelpers.UnsafeEnumCast(obj).GetHashCode();
public static int SbyteEnum<T>(T obj) where T : struct
=> ((sbyte)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
public static int SbyteEnum<T>(T obj) where T : struct => ((sbyte)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
public static int ShortEnum<T>(T obj) where T : struct
=> ((short)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
public static int ShortEnum<T>(T obj) where T : struct => ((short)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
public static int LongEnum<T>(T obj) where T : struct
=> JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
public static int LongEnum<T>(T obj) where T : struct => JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
public static int ValueType<T>(T obj) where T : struct
=> obj.GetHashCode();
public static int ValueType<T>(T obj) where T : struct => obj.GetHashCode();
public static int Nullable<T>(T? obj) where T : struct
=> obj.GetHashCode();
public static unsafe int Nullable<T>(T? obj) where T : struct
=> !obj.HasValue ? 0 : GetHashCode<T>.Default(obj.GetValueOrDefault());
public static int Reference<T>(T obj) where T : class
=> RuntimeHelpers.GetHashCode(obj);
public static int Reference<T>(T obj) where T : class => RuntimeHelpers.GetHashCode(obj);
public static int Object<T>(T obj)
=> obj?.GetHashCode() ?? 0;
public static int Object<T>(T? obj) where T : class => obj?.GetHashCode() ?? 0;
}
internal static unsafe IntPtr MakeDelegate(Type type)
=> _getHashCodeMethods.Where(m => !m.IsGenericMethod)
.FirstOrDefault(m
=> m.GetParameters().First().ParameterType == type)
is { } specializedMethod
? specializedMethod.GetFunctionPointer()
: type.IsNullable(out var genericArgument)
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
: GetMethod(
type.IsEnum
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
switch
{
TypeCode.Int16 => nameof(Methods.ShortEnum),
TypeCode.SByte => nameof(Methods.SbyteEnum),
TypeCode.Byte
or TypeCode.UInt16
or TypeCode.Int32
or TypeCode.UInt32 => nameof(Methods.Enum),
TypeCode.Int64
or TypeCode.UInt64 => nameof(Methods.LongEnum),
_ => ThrowHelper.ThrowNotSupportedException<string>()
}
: type.IsValueType
? nameof(Methods.ValueType)
: nameof(Methods.Object)
).MakeGenericMethod(type).GetFunctionPointer();
private static MethodInfo GetMethod(string name)
=> Array.Find(_getHashCodeMethods, m => m.Name == name);
private static MethodInfo[] _getHashCodeMethods
= typeof(Methods)
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
.Where(m
=> m.ReturnType == typeof(int)
&& m.GetParameters().Length == 1)
.ToArray();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe delegate*<V, int> Cast<T,V>(delegate*<T, int> @delegate)
=> (delegate*<V, int>)@delegate;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe delegate*<T, int> CastVoid<T>(delegate*<void*, int> @delegate)
=> (delegate*<T, int>)@delegate;
public static readonly MethodFactory.HashCode HashCodeMethods
= new(typeof(Methods), "FisheryHashCodeMethod_", false);
}
@@ -3,36 +3,33 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib.FunctionPointers;
public static class GetHashCodeByRef<T>
{
public static readonly unsafe delegate*<ref T, int> Default = (delegate*<ref T, int>)GetHashCodeByRef.MakeDelegate(typeof(T));
public static readonly unsafe delegate*<ref T, int> Default
= (delegate*<ref T, int>)GetHashCodeByRef.HashCodeMethods.GetFunctionPointer(typeof(T));
}
public static class GetHashCodeByRef
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public static class Methods
{
public static int Byte(ref byte obj)
=> obj;
public static int Byte(ref byte obj) => obj;
public static int Int(ref int obj)
=> obj;
public static int Int(ref int obj) => obj;
public static int Uint(ref uint obj)
=> (int)obj;
public static int Uint(ref uint obj) => (int)obj;
public static int Ushort(ref ushort obj)
=> obj;
public static int Ushort(ref ushort obj) => obj;
public static unsafe int VoidPtr(ref void* obj)
=> (int)obj;
public static unsafe int VoidPtr(ref void* obj) => (int)obj;
public static int Enum<T>(ref T obj) where T : struct
=> JitHelpers.UnsafeEnumCast(obj).GetHashCode();
public static int Enum<T>(ref T obj) where T : struct => JitHelpers.UnsafeEnumCast(obj).GetHashCode();
public static int SbyteEnum<T>(ref T obj) where T : struct
=> ((sbyte)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
@@ -40,59 +37,17 @@ public static class GetHashCodeByRef
public static int ShortEnum<T>(ref T obj) where T : struct
=> ((short)JitHelpers.UnsafeEnumCast(obj)).GetHashCode();
public static int LongEnum<T>(ref T obj) where T : struct
=> JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
public static int LongEnum<T>(ref T obj) where T : struct => JitHelpers.UnsafeEnumCastLong(obj).GetHashCode();
public static int ValueType<T>(ref T obj) where T : struct
=> obj.GetHashCode();
public static int ValueType<T>(ref T obj) where T : struct => obj.GetHashCode();
public static int Nullable<T>(ref T? obj) where T : struct
=> obj.GetHashCode();
public static int Nullable<T>(ref T? obj) where T : struct => obj.GetHashCode();
public static int Reference<T>(ref T obj) where T : class
=> RuntimeHelpers.GetHashCode(obj);
public static int Reference<T>(ref T obj) where T : class => RuntimeHelpers.GetHashCode(obj);
public static int Object<T>(ref T obj)
=> obj?.GetHashCode() ?? 0;
public static int Object<T>(ref T? obj) where T : class => obj?.GetHashCode() ?? 0;
}
internal static unsafe IntPtr MakeDelegate(Type type)
=> _getHashCodeMethods.Where(m => !m.IsGenericMethod)
.FirstOrDefault(m
=> m.GetParameters().First().ParameterType.GetElementType() == type)
is { } specializedMethod
? specializedMethod.GetFunctionPointer()
: type.IsNullable(out var genericArgument)
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
? GetMethod(nameof(Methods.Nullable)).MakeGenericMethod(genericArgument).GetFunctionPointer()
: GetMethod(
type.IsEnum
? Type.GetTypeCode(Enum.GetUnderlyingType(type))
switch
{
TypeCode.Int16 => nameof(Methods.ShortEnum),
TypeCode.SByte => nameof(Methods.SbyteEnum),
TypeCode.Byte
or TypeCode.UInt16
or TypeCode.Int32
or TypeCode.UInt32 => nameof(Methods.Enum),
TypeCode.Int64
or TypeCode.UInt64 => nameof(Methods.LongEnum),
_ => ThrowHelper.ThrowNotSupportedException<string>()
}
: type.IsValueType
? nameof(Methods.ValueType)
: nameof(Methods.Object)
).MakeGenericMethod(type).GetFunctionPointer();
private static MethodInfo GetMethod(string name)
=> Array.Find(_getHashCodeMethods, m => m.Name == name);
private static MethodInfo[] _getHashCodeMethods
= typeof(Methods)
.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
.Where(m
=> m.ReturnType == typeof(int)
&& m.GetParameters().Length == 1)
.ToArray();
public static readonly MethodFactory.HashCode HashCodeMethods
= new(typeof(Methods), "FisheryHashCodeByRefMethod_", true);
}
@@ -0,0 +1,393 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v.2.0.If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using System.Security;
namespace FisheryLib.FunctionPointers;
public abstract class MethodFactory
{
protected readonly MethodInfo[] _methods;
protected readonly ConcurrentDictionary<Type, MethodInfo> _methodsByType = new();
protected readonly string _namePrefix;
protected readonly bool _byRef;
public MethodInfo GetGeneric(string name, params Type[] typeArguments)
=> GetNamed(name).MakeGenericMethod(typeArguments);
public MethodInfo GetNamed(string name)
{
foreach (var method in _methods)
{
if (method.Name == name)
return method;
}
return ThrowHelper.ThrowArgumentException<MethodInfo>($"No method found with name: {name}");
}
protected MethodInfo? GetSpecializedMethod(Type type)
{
if (_byRef)
type = type.MakeByRefType();
for (var i = 0; i < _methods.Length; i++)
{
var method = _methods[i];
if (!method.IsGenericMethod && method.GetParameters()[0].ParameterType == type)
return method;
}
return null;
}
public MethodInfo GetForType(Type type)
=> _methodsByType.TryGetValue(type, out var value)
? value
: _methodsByType[type] = GetOrMakeMethodForType(type);
protected abstract MethodInfo GetOrMakeMethodForType(Type type);
[SecuritySafeCritical]
public IntPtr GetFunctionPointer(Type type) => GetForType(type).GetFunctionPointer();
protected static MethodInfo[] GetMethodInfoArray(Type type, Func<MethodInfo, bool> predicate)
=> type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)
.Where(predicate).ToArray();
protected MethodFactory(MethodInfo[] methods, string namePrefix, bool byRef)
{
_byRef = byRef;
_methods = methods;
_namePrefix = namePrefix;
}
public new class Equals : MethodFactory
{
protected override MethodInfo GetOrMakeMethodForType(Type type)
=> GetSpecializedMethod(type)
?? (type.IsAssignableTo(typeof(IEquatable<>), type)
? GetGeneric(type.IsValueType
? nameof(FunctionPointers.Equals.Methods.EquatableValueType)
: nameof(FunctionPointers.Equals.Methods.EquatableReferenceType), type)
: type.IsNullable(out var genericArgument)
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
? GetGeneric(nameof(FunctionPointers.Equals.Methods.Nullable), genericArgument)
: type.IsEnum
? GetGeneric(Type.GetTypeCode(Enum.GetUnderlyingType(type))
switch
{
TypeCode.Int16
or TypeCode.SByte
or TypeCode.Byte
or TypeCode.UInt16
or TypeCode.Int32
or TypeCode.UInt32 => nameof(FunctionPointers.Equals.Methods.Enum),
TypeCode.Int64
or TypeCode.UInt64 => nameof(FunctionPointers.Equals.Methods.LongEnum),
_ => ThrowHelper.ThrowNotSupportedException<string>()
}, type)
: type.IsValueType
? TryGetMethodByName(type) ?? CompileEqualsMethod(type)
: GetGeneric(nameof(FunctionPointers.Equals.Methods.Object), type));
protected MethodInfo? TryGetMethodByName(Type type)
{
var methods = type.GetMethods(AccessTools.allDeclared);
if (_byRef)
type = type.MakeByRefType();
MethodInfo? methodThatNeedsWrapper = null;
for (var i = 0; i < methods.Length; i++)
{
var method = methods[i];
if (method.Name is not ("Equals" or "op_Equality")
|| method.ReturnType != typeof(bool))
continue;
var parameters = method.GetParameters();
var isGood = (parameters.Length == 2 && method.IsStatic) || parameters.Length == 1;
for (var j = 0; j < parameters.Length; j++)
{
if (parameters[j].ParameterType != type)
isGood = false;
}
if (!isGood)
continue;
if (_byRef || method.IsStatic)
return method;
else
methodThatNeedsWrapper = method;
}
return methodThatNeedsWrapper != null ? CompileWrapperMethod(type, methodThatNeedsWrapper) : null;
}
protected MethodInfo CompileWrapperMethod(Type type, MethodInfo method)
{
#if DEBUG
Log.Message($"Method {method.FullDescription()} determined to be in need of wrapper");
#endif
return GetGeneric(nameof(FunctionPointers.Equals.Methods.ValueType), type);
// var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(bool),
// new[] { type, type }, type, true);
// var il = dm.GetILGenerator();
//
// il.Emit(FishTranspiler.ArgumentAddress(0));
// il.Emit(FishTranspiler.Argument(1));
// il.Emit(FishTranspiler.Call(method)); // throws NullRef somehow
// il.Emit(FishTranspiler.Return);
//
// return dm.CreateDelegate(typeof(Func<,,>).MakeGenericType(type, type, typeof(bool))).Method;
}
protected MethodInfo CompileEqualsMethod(Type type)
{
try
{
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(bool),
_byRef ? new[] { type.MakeByRefType(), type.MakeByRefType() } : new[] { type, type }, type, true);
var il = dm.GetILGenerator();
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var needsJump = fields.Length > 1;
var falseLabel = needsJump ? il.DefineLabel() : default;
var trueLabel = needsJump ? il.DefineLabel() : default;
for (var i = 0; i < fields.Length; i++)
{
var field = fields[i];
for (var j = 0; j < 2; j++)
{
il.Emit(FishTranspiler.Argument(j));
il.Emit(FishTranspiler.Field(field));
}
il.Emit(FishTranspiler.Call(GetForType(field.FieldType)));
if (needsJump)
{
il.Emit(i == fields.Length - 1
? FishTranspiler.GoTo_Short(trueLabel)
: FishTranspiler.IfFalse_Short(falseLabel));
}
}
if (needsJump)
{
il.MarkLabel(falseLabel);
il.Emit(FishTranspiler.Constant(0));
il.MarkLabel(trueLabel);
}
il.Emit(FishTranspiler.Return);
return dm.CreateDelegate(
(_byRef ? typeof(ByRefFunc<,,>) : typeof(Func<,,>)).MakeGenericType(type, type, typeof(bool)))
.Method;
}
catch (Exception e)
{
return LogErrorAndReturnFallback(type, e);
}
}
private MethodInfo LogErrorAndReturnFallback(Type type, Exception e)
{
Log.Error($"Failed compiling specialized Equals method for {
type.FullDescription()}. Returning fallback instead.\n{e}{new StackTrace()}");
return GetGeneric(nameof(FunctionPointers.Equals.Methods.ValueType), type);
}
private static MethodInfo[] GetMethodInfoArraySafely(Type type)
{
try
{
return GetMethodInfoArray(type, static m
=> m.ReturnType == typeof(bool)
&& m.GetParameters().Length == 2);
}
catch (Exception e)
{
Log.Error($"Exception while preparing method info array for type {type}:\n{e}");
throw;
}
}
public Equals(Type type, string dynamicMethodNamePrefix, bool byRef)
: base(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
{
}
private delegate TResult ByRefFunc<T1, T2, out TResult>(ref T1 arg1, ref T2 arg2);
}
public class HashCode : MethodFactory
{
protected override MethodInfo GetOrMakeMethodForType(Type type)
=> GetSpecializedMethod(type)
?? (type.IsNullable(out var genericArgument)
&& genericArgument.IsAssignableTo(typeof(IEquatable<>), genericArgument)
? GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.Nullable), genericArgument)
: type.IsEnum
? GetGeneric(Type.GetTypeCode(Enum.GetUnderlyingType(type))
switch
{
TypeCode.Int16 => nameof(FunctionPointers.GetHashCode.Methods.ShortEnum),
TypeCode.SByte => nameof(FunctionPointers.GetHashCode.Methods.SbyteEnum),
TypeCode.Byte
or TypeCode.UInt16
or TypeCode.Int32
or TypeCode.UInt32 => nameof(FunctionPointers.GetHashCode.Methods.Enum),
TypeCode.Int64
or TypeCode.UInt64 => nameof(FunctionPointers.GetHashCode.Methods.LongEnum),
_ => ThrowHelper.ThrowNotSupportedException<string>()
}, type)
: type.IsValueType
? TryGetMethodByName(type) ?? CompileHashCodeMethod(type)
: GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.Object), type));
protected MethodInfo? TryGetMethodByName(Type type)
{
var methods = type.GetMethods(AccessTools.allDeclared);
if (_byRef)
type = type.MakeByRefType();
MethodInfo? methodThatNeedsWrapper = null;
for (var i = 0; i < methods.Length; i++)
{
var method = methods[i];
if (method.Name != "GetHashCode"
|| method.ReturnType != typeof(int))
continue;
var parameters = method.GetParameters();
var isGood = (parameters.Length == 1 && method.IsStatic) || parameters.Length == 0;
for (var j = 0; j < parameters.Length; j++)
{
if (parameters[j].ParameterType != type)
isGood = false;
}
if (!isGood)
continue;
if (_byRef || method.IsStatic)
return method;
else
methodThatNeedsWrapper = method;
}
return methodThatNeedsWrapper != null ? CompileWrapperMethod(type, methodThatNeedsWrapper) : null;
}
protected MethodInfo CompileWrapperMethod(Type type, MethodInfo method)
{
#if DEBUG
Log.Message($"Method {method.FullDescription()} determined to be in need of wrapper");
#endif
return GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.ValueType), type);
// var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(int),
// new[] { type }, type, true);
// var il = dm.GetILGenerator();
//
// il.Emit(FishTranspiler.ArgumentAddress(0));
// il.Emit(FishTranspiler.Call(method));
// il.Emit(FishTranspiler.Return);
//
// return dm.CreateDelegate(typeof(HashCodeGetter<>).MakeGenericType(type)).Method;
}
private MethodInfo CompileHashCodeMethod(Type type)
{
try
{
var dm = new DynamicMethod($"{_namePrefix}{type.Name}", typeof(int),
_byRef ? new[] { type.MakeByRefType() } : new[] { type }, type, true);
var il = dm.GetILGenerator();
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (var i = 0; i < fields.Length; i++)
{
var field = fields[i];
il.Emit(FishTranspiler.Argument(0));
il.Emit(FishTranspiler.Field(field));
if (field.FieldType != typeof(int))
il.Emit(FishTranspiler.Call(GetForType(field.FieldType)));
if (i < 1)
continue;
il.Emit(FishTranspiler.Call<Func<int, int, int>>(i == 1
? global::FisheryLib.HashCode.Combine
: global::FisheryLib.HashCode.CombineWith));
}
il.Emit(FishTranspiler.Return);
return dm.CreateDelegate((_byRef ? typeof(ByRefHashCodeGetter<>) : typeof(HashCodeGetter<>))
.MakeGenericType(type)).Method;
}
catch (Exception e)
{
return LogErrorAndReturnFallback(type, e);
}
}
private MethodInfo LogErrorAndReturnFallback(Type type, Exception e)
{
Log.Error($"Failed compiling specialized GetHashCode method for {
type.FullDescription()}. Returning fallback instead.\n{e}{new StackTrace()}");
return GetGeneric(nameof(FunctionPointers.GetHashCode.Methods.ValueType), type);
}
private static MethodInfo[] GetMethodInfoArraySafely(Type type)
{
try
{
return GetMethodInfoArray(type, static m
=> m.ReturnType == typeof(int)
&& m.GetParameters().Length == 1);
}
catch (Exception e)
{
Log.Error($"Exception while preparing method info array for type {type}:\n{e}");
throw;
}
}
public HashCode(Type type, string dynamicMethodNamePrefix, bool byRef)
: base(GetMethodInfoArraySafely(type), dynamicMethodNamePrefix, byRef)
{
}
private delegate int ByRefHashCodeGetter<T>(ref T arg);
private delegate int HashCodeGetter<in T>(T arg);
}
}
@@ -28,3 +28,4 @@ public static class HarmonyPatchExtensions
return patchProcessor.Patch();
}
}*/
+10 -12
View File
@@ -7,24 +7,23 @@ using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace FisheryLib;
public static class HashCode
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe int Get<T>([AllowNull] T obj)
=> FunctionPointers.GetHashCode<T>.Default(obj!);
public static unsafe int Get<T>([AllowNull] T obj) => FunctionPointers.GetHashCode<T>.Default(obj!);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe int Get<T>([AllowNull] ref T obj)
=> FunctionPointers.GetHashCodeByRef<T>.Default(ref obj!);
public static unsafe int Get<T>([AllowNull] ref T obj) => FunctionPointers.GetHashCodeByRef<T>.Default(ref obj!);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Combine<T_first, T_second>(T_first first, T_second second)
//where T_first : notnull where T_second : notnull
=> Combine(Get(first), Get(second));
// https://stackoverflow.com/questions/1646807/quick-and-simple-hash-code-combinations/34006336#34006336
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Combine(int first, int second)
=> unchecked((1009 * 9176) + first).CombineWith(second); //https://stackoverflow.com/questions/1646807/quick-and-simple-hash-code-combinations/34006336#34006336
public static int Combine(int first, int second) => unchecked((1009 * 9176) + first).CombineWith(second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Combine<T_first, T_second, T_third>(T_first first, T_second second, T_third third)
@@ -32,11 +31,11 @@ public static class HashCode
=> Combine(first, second).CombineWith(third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Combine(int first, int second, int third)
=> Combine(first, second).CombineWith(third);
public static int Combine(int first, int second, int third) => Combine(first, second).CombineWith(third);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Combine<T_first, T_second, T_third, T_fourth>(T_first first, T_second second, T_third third, T_fourth fourth)
public static int Combine<T_first, T_second, T_third, T_fourth>(T_first first, T_second second, T_third third,
T_fourth fourth)
//where T_first : notnull where T_second : notnull where T_third : notnull where T_fourth : notnull
=> Combine(first, second, third).CombineWith(fourth);
@@ -45,11 +44,10 @@ public static class HashCode
=> Combine(first, second, third).CombineWith(fourth);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int CombineWith(this int first, int second)
=> unchecked((first * 9176) + second);
internal static int CombineWith(this int first, int second) => unchecked((first * 9176) + second);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int CombineWith<T>(this int first, T second)
internal static int CombineWith<T>(this int first, T second)
//where T : notnull
=> unchecked((first * 9176) + Get(second));
}
+1
View File
@@ -336,3 +336,4 @@ public class InfixReplaceAttribute : InfixAttribute
: base(new(NotNull(predicate), InfixTargetMode.Replace))
{ }
}*/
+12 -10
View File
@@ -3,22 +3,24 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
public static class Log
{
public static void Message(string message) => _message(message);
public static void Warning(string message) => _warning(message);
public static void Error(string message) => _error(message);
public static void Message(string message) => Config.Message(message);
public static void Warning(string message) => Config.Warning(message);
public static void Error(string message) => Config.Error(message);
[PublicAPI]
public static class Config
{
public static Action<string> Message { set => _message = value; }
public static Action<string> Warning { set => _warning = value; }
public static Action<string> Error { set => _error = value; }
}
public static Action<string> Message { get; set; } = static text => Verse.Log.Message(text);
private static Action<string> _message = Verse.Log.Message;
private static Action<string> _warning = Verse.Log.Warning;
private static Action<string> _error = Verse.Log.Error;
public static Action<string> Warning { get; set; } = static text => Verse.Log.Warning(text);
public static Action<string> Error { get; set; } = static text => Verse.Log.Error(text);
}
}
+48 -69
View File
@@ -7,10 +7,12 @@ using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace FisheryLib;
/// <summary>
/// Provides a collection of methods for interoperating with <see cref="Memory{T}"/>, <see cref="ReadOnlyMemory{T}"/>,
/// <see cref="Span{T}"/>, and <see cref="ReadOnlySpan{T}"/>.
/// </summary>
#pragma warning disable CS8500
internal static class MemoryMarshal
{
/// <summary>
@@ -31,9 +33,8 @@ internal static class MemoryMarshal
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
return new Span<byte>(
Unsafe.AsPointer(ref GetReference(span)),
checked(span.Length * Unsafe.SizeOf<T>()));
return new(Unsafe.AsPointer(ref GetReference(span)),
checked(span.Length * sizeof(T)));
}
/// <summary>
@@ -54,9 +55,8 @@ internal static class MemoryMarshal
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
return new ReadOnlySpan<byte>(
Unsafe.AsPointer(ref GetReference(span)),
checked(span.Length * Unsafe.SizeOf<T>()));
return new(Unsafe.AsPointer(ref GetReference(span)),
checked(span.Length * sizeof(T)));
}
/// <summary>Creates a <see cref="Memory{T}"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
@@ -69,8 +69,8 @@ internal static class MemoryMarshal
/// as <see cref="Memory{T}"/> but only used for reading to store a <see cref="ReadOnlyMemory{T}"/>.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> memory) =>
Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref memory);
public static Memory<T> AsMemory<T>(ReadOnlyMemory<T> memory)
=> Unsafe.As<ReadOnlyMemory<T>, Memory<T>>(ref memory);
/// <summary>
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
@@ -91,14 +91,16 @@ internal static class MemoryMarshal
/// for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe ref T GetNonNullPinnableReference<T>(Span<T> span) => ref (span.Length != 0) ? ref span.DangerousGetPinnableReference() : ref Unsafe.AsRef<T>((void*)1);
internal static unsafe ref T GetNonNullPinnableReference<T>(Span<T> span)
=> ref span.Length != 0 ? ref span.DangerousGetPinnableReference() : ref Unsafe.AsRef<T>((void*)1);
/// <summary>
/// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to fake non-null pointer. Such a reference
/// can be used for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe ref T GetNonNullPinnableReference<T>(ReadOnlySpan<T> span) => ref (span.Length != 0) ? ref span.DangerousGetPinnableReference() : ref Unsafe.AsRef<T>((void*)1);
internal static unsafe ref T GetNonNullPinnableReference<T>(ReadOnlySpan<T> span)
=> ref span.Length != 0 ? ref span.DangerousGetPinnableReference() : ref Unsafe.AsRef<T>((void*)1);
/// <summary>
/// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
@@ -112,7 +114,7 @@ internal static class MemoryMarshal
/// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span)
public static unsafe Span<TTo> Cast<TFrom, TTo>(Span<TFrom> span)
where TFrom : struct
where TTo : struct
{
@@ -123,8 +125,8 @@ internal static class MemoryMarshal
// Use unsigned integers - unsigned division by constant (especially by power of 2)
// and checked casts are faster and smaller.
var fromSize = (uint)Unsafe.SizeOf<TFrom>();
var toSize = (uint)Unsafe.SizeOf<TTo>();
var fromSize = (uint)sizeof(TFrom);
var toSize = (uint)sizeof(TTo);
var fromLength = (uint)span.Length;
int toLength;
if (fromSize == toSize)
@@ -146,16 +148,11 @@ internal static class MemoryMarshal
// Ensure that casts are done in such a way that the JIT is able to "see"
// the uint->ulong casts and the multiply together so that on 32 bit targets
// 32x32to64 multiplication is used.
var toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
var toLengthUInt64 = (ulong)fromLength * fromSize / toSize;
toLength = checked((int)toLengthUInt64);
}
unsafe
{
return new Span<TTo>(
Unsafe.AsPointer(ref span.DangerousGetPinnableReference()),
toLength);
}
return new(Unsafe.AsPointer(ref span.DangerousGetPinnableReference()), toLength);
}
/// <summary>
@@ -170,7 +167,7 @@ internal static class MemoryMarshal
/// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span)
public static unsafe ReadOnlySpan<TTo> Cast<TFrom, TTo>(ReadOnlySpan<TFrom> span)
where TFrom : struct
where TTo : struct
{
@@ -181,8 +178,8 @@ internal static class MemoryMarshal
// Use unsigned integers - unsigned division by constant (especially by power of 2)
// and checked casts are faster and smaller.
var fromSize = (uint)Unsafe.SizeOf<TFrom>();
var toSize = (uint)Unsafe.SizeOf<TTo>();
var fromSize = (uint)sizeof(TFrom);
var toSize = (uint)sizeof(TTo);
var fromLength = (uint)span.Length;
int toLength;
if (fromSize == toSize)
@@ -204,15 +201,11 @@ internal static class MemoryMarshal
// Ensure that casts are done in such a way that the JIT is able to "see"
// the uint->ulong casts and the multiply together so that on 32 bit targets
// 32x32to64 multiplication is used.
var toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
var toLengthUInt64 = (ulong)fromLength * fromSize / toSize;
toLength = checked((int)toLengthUInt64);
}
unsafe
{
return new ReadOnlySpan<TTo>(
Unsafe.AsPointer(ref GetReference(span)),
toLength);
}
return new(Unsafe.AsPointer(ref GetReference(span)), toLength);
}
/// <summary>
@@ -225,7 +218,8 @@ internal static class MemoryMarshal
/// <returns>A span representing the specified reference and length.</returns>
/// <remarks>The lifetime of the returned span will not be validated for safety by span-aware languages.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe Span<T> CreateSpan<T>(ref T reference, int length) => new(Unsafe.AsPointer(ref reference), length);
public static unsafe Span<T> CreateSpan<T>(ref T reference, int length)
=> new(Unsafe.AsPointer(ref reference), length);
/// <summary>
/// Creates a new read-only span over a portion of a regular managed object. This can be useful
@@ -237,7 +231,8 @@ internal static class MemoryMarshal
/// <returns>A read-only span representing the specified reference and length.</returns>
/// <remarks>The lifetime of the returned span will not be validated for safety by span-aware languages.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length) => new(Unsafe.AsPointer(ref reference), length);
public static unsafe ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
=> new(Unsafe.AsPointer(ref reference), length);
/*
/// <summary>Creates a new read-only span for a null-terminated string.</summary>
@@ -408,17 +403,13 @@ internal static class MemoryMarshal
/// Reads a structure of type T out of a read-only span of bytes.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Read<T>(ReadOnlySpan<byte> source)
public static unsafe T Read<T>(ReadOnlySpan<byte> source)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf<T>() > source.Length)
{
if (sizeof(T) > source.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
return Unsafe.ReadUnaligned<T>(ref GetReference(source));
}
@@ -427,18 +418,17 @@ internal static class MemoryMarshal
/// <returns>If the span is too small to contain the type T, return false.</returns>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryRead<T>(ReadOnlySpan<byte> source, out T value)
public static unsafe bool TryRead<T>(ReadOnlySpan<byte> source, out T value)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf<T>() > (uint)source.Length)
if (sizeof(T) > (uint)source.Length)
{
value = default;
return false;
}
value = Unsafe.ReadUnaligned<T>(ref GetReference(source));
return true;
}
@@ -447,18 +437,14 @@ internal static class MemoryMarshal
/// Writes a structure of type T into a span of bytes.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Write<T>(Span<byte> destination, ref T value)
public static unsafe void Write<T>(Span<byte> destination, ref T value)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if ((uint)Unsafe.SizeOf<T>() > (uint)destination.Length)
{
if ((uint)sizeof(T) > (uint)destination.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
Unsafe.WriteUnaligned<T>(ref GetReference(destination), value);
Unsafe.WriteUnaligned(ref GetReference(destination), value);
}
/// <summary>
@@ -466,17 +452,14 @@ internal static class MemoryMarshal
/// <returns>If the span is too small to contain the type T, return false.</returns>
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryWrite<T>(Span<byte> destination, ref T value)
public static unsafe bool TryWrite<T>(Span<byte> destination, ref T value)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf<T>() > (uint)destination.Length)
{
if (sizeof(T) > (uint)destination.Length)
return false;
}
Unsafe.WriteUnaligned(ref GetReference(destination), value);
return true;
}
@@ -489,17 +472,13 @@ internal static class MemoryMarshal
/// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T AsRef<T>(Span<byte> span)
public static unsafe ref T AsRef<T>(Span<byte> span)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf<T>() > (uint)span.Length)
{
if (sizeof(T) > (uint)span.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
return ref Unsafe.As<byte, T>(ref GetReference(span));
}
@@ -511,17 +490,13 @@ internal static class MemoryMarshal
/// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means.
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref readonly T AsRef<T>(ReadOnlySpan<byte> span)
public static unsafe ref readonly T AsRef<T>(ReadOnlySpan<byte> span)
where T : struct
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
ThrowInvalidTypeWithPointersNotSupported(typeof(T));
}
if (Unsafe.SizeOf<T>() > (uint)span.Length)
{
if (sizeof(T) > (uint)span.Length)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
return ref Unsafe.As<byte, T>(ref GetReference(span));
}
@@ -549,15 +524,19 @@ internal static class MemoryMarshal
ThrowHelper.ThrowArgumentOutOfRangeException();
return default;
}
if (!typeof(T).IsValueType && array.GetType() != typeof(T[]))
ThrowHelper.ThrowArrayTypeMismatchException();
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
ThrowHelper.ThrowArgumentOutOfRangeException();
// Before using _index, check if _index < 0, then 'and' it with RemoveFlagsBitMask
return new Memory<T>(/*(object)*/array, start | (1 << 31), length);
return new( /*(object)*/array, start | (1 << 31), length);
}
[DoesNotReturn]
private static void ThrowInvalidTypeWithPointersNotSupported(Type targetType)
=> throw new ArgumentException($"Cannot use type '{targetType}'.Only value types without pointers or references are supported.");
=> throw new ArgumentException(
$"Cannot use type '{targetType}'.Only value types without pointers or references are supported.");
}
#pragma warning restore CS8500
+4 -3
View File
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
@@ -14,6 +15,7 @@ namespace FisheryLib;
/// </summary>
/// <typeparam name="T">The type of items to enumerate.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
[PublicAPI]
public ref struct ReadOnlySpanEnumerable<T>
{
/// <summary>
@@ -58,7 +60,7 @@ public ref struct ReadOnlySpanEnumerable<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
// See comment in SpanEnumerable<T> about this
// See comment in SpanEnumerable<T> about this
=> new(ref Unsafe.Add(ref _span.DangerousGetPinnableReference(), (nint)(uint)_index), _index);
}
@@ -79,8 +81,7 @@ public ref struct ReadOnlySpanEnumerable<T>
/// <param name="value">A reference to the target value.</param>
/// <param name="index">The index of the target value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Item(ref T value, int index)
=> _span = MemoryMarshal.CreateReadOnlySpan(ref value, index);
public Item(ref T value, int index) => _span = MemoryMarshal.CreateReadOnlySpan(ref value, index);
/// <summary>
/// Gets the reference to the current value.
+21 -20
View File
@@ -8,8 +8,12 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
#pragma warning disable CS8500
public static class ReadOnlySpanExtensions
{
/// <summary>
@@ -67,14 +71,14 @@ public static class ReadOnlySpanExtensions
=> ref Unsafe.Add(ref span.DangerousGetPinnableReference(), i);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T First<T>(this ReadOnlySpan<T> span)
=> span[0];
public static T First<T>(this ReadOnlySpan<T> span) => span[0];
[return: MaybeNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T FirstOrDefault<T>(this ReadOnlySpan<T> span)
=> !span.IsEmpty ? span[0]
: default;
=> !span.IsEmpty
? span[0]
: default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe TSource First<TSource>(this ReadOnlySpan<TSource> span, delegate*<TSource, bool> predicate)
@@ -87,6 +91,7 @@ public static class ReadOnlySpanExtensions
{
if (predicate(Unsafe.Add(ref r0, offset)))
goto returnForOffset0;
if (predicate(Unsafe.Add(ref r0, offset + 1)))
return Unsafe.Add(ref r0, offset + 1);
if (predicate(Unsafe.Add(ref r0, offset + 2)))
@@ -124,6 +129,7 @@ public static class ReadOnlySpanExtensions
{
if (predicate(Unsafe.Add(ref r0, offset)))
goto returnForOffset0;
if (predicate(Unsafe.Add(ref r0, offset + 1)))
return Unsafe.Add(ref r0, offset + 1);
if (predicate(Unsafe.Add(ref r0, offset + 2)))
@@ -155,7 +161,8 @@ public static class ReadOnlySpanExtensions
[return: MaybeNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe TSource FirstOrDefault<TSource>(this ReadOnlySpan<TSource> span, delegate*<TSource, bool> predicate)
public static unsafe TSource FirstOrDefault<TSource>(this ReadOnlySpan<TSource> span,
delegate*<TSource, bool> predicate)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
@@ -165,6 +172,7 @@ public static class ReadOnlySpanExtensions
{
if (predicate(Unsafe.Add(ref r0, offset)))
goto returnForOffset0;
if (predicate(Unsafe.Add(ref r0, offset + 1)))
return Unsafe.Add(ref r0, offset + 1);
if (predicate(Unsafe.Add(ref r0, offset + 2)))
@@ -203,6 +211,7 @@ public static class ReadOnlySpanExtensions
{
if (predicate(Unsafe.Add(ref r0, offset)))
goto returnForOffset0;
if (predicate(Unsafe.Add(ref r0, offset + 1)))
return Unsafe.Add(ref r0, offset + 1);
if (predicate(Unsafe.Add(ref r0, offset + 2)))
@@ -230,8 +239,7 @@ public static class ReadOnlySpanExtensions
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Any<TSource>(this ReadOnlySpan<TSource> span)
=> !span.IsEmpty;
public static bool Any<TSource>(this ReadOnlySpan<TSource> span) => !span.IsEmpty;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool Any<TSource>(this ReadOnlySpan<TSource> span, delegate*<TSource, bool> predicate)
@@ -246,9 +254,7 @@ public static class ReadOnlySpanExtensions
|| predicate(Unsafe.Add(ref r0, offset + 1))
|| predicate(Unsafe.Add(ref r0, offset + 2))
|| predicate(Unsafe.Add(ref r0, offset + 3)))
{
return true;
}
length -= 4;
offset += 4;
@@ -279,9 +285,7 @@ public static class ReadOnlySpanExtensions
|| predicate(Unsafe.Add(ref r0, offset + 1))
|| predicate(Unsafe.Add(ref r0, offset + 2))
|| predicate(Unsafe.Add(ref r0, offset + 3)))
{
return true;
}
length -= 4;
offset += 4;
@@ -351,9 +355,7 @@ public static class ReadOnlySpanExtensions
|| Unsafe.Add(ref r0, offset + 1).Equals<T>(value)
|| Unsafe.Add(ref r0, offset + 2).Equals<T>(value)
|| Unsafe.Add(ref r0, offset + 3).Equals<T>(value))
{
return true;
}
length -= 4;
offset += 4;
@@ -438,14 +440,15 @@ public static class ReadOnlySpanReferenceExtensions
/// <param name="reference">The reference to the target item to get the index for.</param>
/// <returns>The index of <paramref name="reference"/> within <paramref name="span"/>, or <c>-1</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf<T>(this ReadOnlySpan<T> span, ref T reference)
public static unsafe int IndexOf<T>(this ReadOnlySpan<T> span, ref T reference)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var byteOffset = Unsafe.ByteOffset(ref r0, ref reference);
var elementOffset = byteOffset / (nint)(uint)Unsafe.SizeOf<T>();
var elementOffset = byteOffset / (nint)(uint)sizeof(T);
return (nuint)elementOffset >= (uint)span.Length ? -1
return (nuint)elementOffset >= (uint)span.Length
? -1
: (int)elementOffset;
}
@@ -457,8 +460,7 @@ public static class ReadOnlySpanReferenceExtensions
/// <param name="reference">The reference to the target item to locate.</param>
/// <returns>true if the reference points towards an element of <see cref="ReadOnlySpan{T}"/>; otherwise, false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains<T>(this ReadOnlySpan<T> span, ref T reference)
=> span.IndexOf(ref reference) >= 0;
public static bool Contains<T>(this ReadOnlySpan<T> span, ref T reference) => span.IndexOf(ref reference) >= 0;
}
public static class ReadOnlySpanStructExtensions
@@ -517,9 +519,7 @@ public static class ReadOnlySpanStructExtensions
|| Unsafe.Add(ref r0, offset + 1).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.Add(ref r0, offset + 2).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.Add(ref r0, offset + 3).Equals(ref Unsafe.AsRef(in value)))
{
return true;
}
length -= 4;
offset += 4;
@@ -572,3 +572,4 @@ public static class ReadOnlySpanStructExtensions
return -1;
}
}
#pragma warning restore CS8500
+393 -75
View File
@@ -3,27 +3,33 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using Mono.Cecil;
using MonoMod.Utils;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Verse;
using static Mono.Cecil.Mixin;
namespace FisheryLib;
[PublicAPI]
public static class Reflection
{
public static CodeInstructions GetCodeInstructions(Delegate method)
=> PatchProcessor.GetCurrentInstructions(method.Method);
public static CodeInstructions GetCodeInstructions(Delegate method, ILGenerator? generator = null)
=> GetCodeInstructions(method.Method, generator);
public static CodeInstructions GetCodeInstructions(Expression<Action> method)
=> PatchProcessor.GetCurrentInstructions(SymbolExtensions.GetMethodInfo(method));
public static CodeInstructions GetCodeInstructions(Expression<Action> method, ILGenerator? generator = null)
=> GetCodeInstructions(SymbolExtensions.GetMethodInfo(method), generator);
public static CodeInstructions MakeReplacementCall(Delegate method)
=> MakeReplacementCall(method.Method);
public static CodeInstructions GetCodeInstructions(MethodInfo method, ILGenerator? generator = null)
=> PatchProcessor.GetCurrentInstructions(method, generator: generator);
public static CodeInstructions MakeReplacementCall(Delegate method) => MakeReplacementCall(method.Method);
public static CodeInstructions MakeReplacementCall(MethodInfo method)
{
@@ -37,29 +43,57 @@ public static class Reflection
yield return FishTranspiler.Return;
}
public static Type Type(string assembly, string type) => System.Type.GetType($"{type}, {assembly}");
public static Type? Type(string assembly, string type) => System.Type.GetType($"{type}, {assembly}");
public static MethodInfo MethodInfo<T>(T method) where T : Delegate => method.Method;
public static MethodInfo MethodInfo(string assembly, string type, string name, Type[]? parameters = null, Type[]? generics = null)
public static MethodInfo? MethodInfo(string assembly, string type, string name, Type[]? parameters = null,
Type[]? generics = null)
=> AccessTools.Method(Type(assembly, type), name, parameters, generics);
public static IEnumerable<MethodInfo> MethodsOfName(Type type, string name)
=> MethodsOfNameInternal(type, name, true);
private static void ThrowForMethodsOfName(Type? type, string name)
=> ThrowHelper.ThrowArgumentException($"No methods found for {type.FullDescription()}:{name}");
public static IEnumerable<MethodInfo> MethodsOfNameSilentFail(Type? type, string name)
=> MethodsOfNameInternal(type, name, false);
private static IEnumerable<MethodInfo> MethodsOfNameInternal(Type? type, string name, bool throwWhenEmpty)
{
var methods = MethodsOfNameSilentFail(type, name).ToArray();
if (!methods.Any())
var methods = type?.GetMethods(AccessTools.allDeclared);
if (methods is null)
{
if (throwWhenEmpty)
ThrowForMethodsOfName(type, name);
yield break;
}
var count = 0;
for (var i = 0; i < methods.Length; i++)
{
if (methods[i].Name != name)
continue;
count++;
yield return methods[i];
}
if (throwWhenEmpty && count < 1)
ThrowForMethodsOfName(type, name);
return methods;
}
private static void ThrowForMethodsOfName(Type type, string name) => ThrowHelper.ThrowArgumentException($"No methods found for {type.FullDescription()}:{name}");
public static FieldInfo? FieldInfo(string assembly, string type, string name)
=> AccessTools.Field(Type(assembly, type), name);
public static IEnumerable<MethodInfo> MethodsOfNameSilentFail(Type type, string name)
=> type?.GetMethods(AccessTools.allDeclared)
.Where(m => m.Name == name)
?? Enumerable.Empty<MethodInfo>();
public static MethodInfo? GetGenericMethod(this Type type, string name, params Type[] typeArguments)
=> type.GetMethod(name)?.MakeGenericMethod(typeArguments);
public static FieldInfo FieldInfo(string assembly, string type, string name) => AccessTools.Field(Type(assembly, type), name);
public static MethodInfo? GetGenericMethod(this Type type, string name, BindingFlags bindingAttr,
params Type[] typeArguments)
=> type.GetMethod(name, bindingAttr)?.MakeGenericMethod(typeArguments);
public static bool IsAssignableTo(this Type type, [NotNullWhen(true)] Type? targetType, params Type[] generics)
{
@@ -86,10 +120,8 @@ public static class Reflection
while (typesEnumerator.MoveNext())
{
if (!targetTypesEnumerator.MoveNext()
|| !targetTypesEnumerator.Current.IsAssignableFrom(typesEnumerator.Current))
{
|| !targetTypesEnumerator.Current!.IsAssignableFrom(typesEnumerator.Current))
return false;
}
}
return !targetTypesEnumerator.MoveNext();
@@ -117,77 +149,298 @@ public static class Reflection
}
}
public static Type GetDelegateReturnType(this Type type)
{
Guard.IsTypeAssignableToType(type, typeof(Delegate));
return type.GetMethod("Invoke")!.ReturnType;
}
public static ParameterInfo[] GetDelegateParameters(this Type type)
{
Guard.IsTypeAssignableToType(type, typeof(Delegate));
return type.GetMethod("Invoke")!.GetParameters();
}
public static bool HasGenericInterfaceDefinition(this Type type, Type interfaceType)
{
Guard.IsNotNull(type);
return Array.Exists(type.GetInterfaces(), @interface
=> @interface.IsGenericType
&& @interface.GetGenericTypeDefinition() == interfaceType);
=> @interface.IsGenericType
&& @interface.GetGenericTypeDefinition() == interfaceType);
}
public static unsafe IntPtr GetFunctionPointer(this MethodBase method) => method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T> GetFunctionPointer<T>(this MethodBase method) => (delegate*<T>)method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T_in, T_out> GetFunctionPointer<T_in, T_out>(this MethodBase method) => (delegate*<T_in, T_out>)method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T1, T2, T_out> GetFunctionPointer<T1, T2, T_out>(this MethodBase method) => (delegate*<T1, T2, T_out>)method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T1, T2, T3, T_out> GetFunctionPointer<T1, T2, T3, T_out>(this MethodBase method) => (delegate*<T1, T2, T3, T_out>)method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T1, T2, T3, T4, T_out> GetFunctionPointer<T1, T2, T3, T4, T_out>(this MethodBase method) => (delegate*<T1, T2, T3, T4, T_out>)method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T1, T2, T3, T4, T5, T_out> GetFunctionPointer<T1, T2, T3, T4, T5, T_out>(this MethodBase method) => (delegate*<T1, T2, T3, T4, T5, T_out>)method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T1, T2, T3, T4, T5, T6, T_out> GetFunctionPointer<T1, T2, T3, T4, T5, T6, T_out>(this MethodBase method) => (delegate*<T1, T2, T3, T4, T5, T6, T_out>)method.MethodHandle.GetFunctionPointer();
public static unsafe delegate*<T1, T2, T3, T4, T5, T6, T7, T_out> GetFunctionPointer<T1, T2, T3, T4, T5, T6, T7, T_out>(this MethodBase method) => (delegate*<T1, T2, T3, T4, T5, T6, T7, T_out>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static IntPtr GetFunctionPointer(this MethodBase method) => method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T> GetFunctionPointer<T>(this MethodBase method)
=> (delegate*<T>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T_in, T_out> GetFunctionPointer<T_in, T_out>(this MethodBase method)
=> (delegate*<T_in, T_out>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T1, T2, T_out> GetFunctionPointer<T1, T2, T_out>(this MethodBase method)
=> (delegate*<T1, T2, T_out>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T1, T2, T3, T_out> GetFunctionPointer<T1, T2, T3, T_out>(this MethodBase method)
=> (delegate*<T1, T2, T3, T_out>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T1, T2, T3, T4, T_out>
GetFunctionPointer<T1, T2, T3, T4, T_out>(this MethodBase method)
=> (delegate*<T1, T2, T3, T4, T_out>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T1, T2, T3, T4, T5, T_out>
GetFunctionPointer<T1, T2, T3, T4, T5, T_out>(this MethodBase method)
=> (delegate*<T1, T2, T3, T4, T5, T_out>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T1, T2, T3, T4, T5, T6, T_out>
GetFunctionPointer<T1, T2, T3, T4, T5, T6, T_out>(this MethodBase method)
=> (delegate*<T1, T2, T3, T4, T5, T6, T_out>)method.MethodHandle.GetFunctionPointer();
[SecuritySafeCritical]
public static unsafe delegate*<T1, T2, T3, T4, T5, T6, T7, T_out>
GetFunctionPointer<T1, T2, T3, T4, T5, T6, T7, T_out>(this MethodBase method)
=> (delegate*<T1, T2, T3, T4, T5, T6, T7, T_out>)method.MethodHandle.GetFunctionPointer();
/// <summary>
/// Finds a constructor with parameters that are either assignable to or assignable from the types in the provided parameters array
/// Finds a constructor with parameters that are either assignable to or assignable from the types in the provided
/// parameters array
/// </summary>
/// <param name="type">The type of the object to construct</param>
/// <param name="parameters">An array of parameter types that are either assignable to or from the parameters of a constructor for the target type</param>
/// <param name="parameters">
/// An array of parameter types that are either assignable to or from the parameters of a constructor for the
/// target type
/// </param>
/// <param name="searchForStatic">Search for static constructors instead of instance constructors</param>
/// <param name="throwOnFailure">Throw an exception on failure to find a matching constructor</param>
/// <returns>A ConstructorInfo that best matches the provided arguments. Null on failure if throwOnFailure is set to false</returns>
public static ConstructorInfo? MatchingConstructor(Type type, Type[]? parameters = null, bool searchForStatic = false, bool throwOnFailure = true)
/// <returns>
/// A ConstructorInfo that best matches the provided arguments. Null on failure if throwOnFailure is set to false
/// </returns>
public static ConstructorInfo? MatchingConstructor(Type type, Type[]? parameters = null,
bool searchForStatic = false, bool throwOnFailure = true)
{
Guard.IsNotNull(type);
parameters ??= Array.Empty<Type>();
var flags = searchForStatic ? (AccessTools.all & ~BindingFlags.Instance) : (AccessTools.all & ~BindingFlags.Static);
var flags = searchForStatic ? AccessTools.all & ~BindingFlags.Instance : AccessTools.all & ~BindingFlags.Static;
return TryGetConstructor(type, flags, paramTypes => paramTypes.AreAssignableFrom(parameters))
?? TryGetConstructor(type, flags, paramTypes => paramTypes.AreAssignableTo(parameters))
?? (throwOnFailure ? ThrowHelper.ThrowInvalidOperationException<ConstructorInfo>(
$"No constructor found for type: {type}, parameters: {parameters.ToStringSafeEnumerable()}, static: {searchForStatic}, found constructors: {type.GetConstructors(flags).ToStringSafeEnumerable()}")
: null);
?? (throwOnFailure
? ThrowHelper.ThrowInvalidOperationException<ConstructorInfo>(
$"No constructor found for type: {type}, parameters: {
parameters.ToStringSafeEnumerable()}, static: {searchForStatic}, found constructors: {
type.GetConstructors(flags).ToStringSafeEnumerable()}")
: null);
}
private static ConstructorInfo TryGetConstructor(Type type, BindingFlags flags, Predicate<IEnumerable<Type>> predicate)
private static ConstructorInfo? TryGetConstructor(Type type, BindingFlags flags,
Predicate<IEnumerable<Type>> predicate)
=> type.GetConstructors(flags).FirstOrDefault(c
=> predicate(c.GetParameters().Select(p => p.ParameterType)));
=> predicate(c.GetParameters().Select(static p => p.ParameterType)));
private static Delegate MakeStructDefaultConstructor(Type type)
public static T? CreateConstructorDelegate<T>(Type type, params Type[] parameters) where T : Delegate
{
var dm = new DynamicMethod("StructDefaultConstructor", type, Array.Empty<Type>(), type, true);
var il = dm.GetILGenerator();
try
{
Guard.IsTypeAssignableToType(typeof(T), typeof(Delegate));
var delegateReturnType = typeof(T).GetDelegateReturnType();
var variable = il.DeclareLocal(type);
var dm = new DynamicMethod($"FisheryConstructorFunc_{type.Name}{parameters.Length.ToString()}",
delegateReturnType, parameters, type, true);
var il = dm.GetILGenerator();
il.Emit(FishTranspiler.LocalVariable(variable).Address());
il.Emit(FishTranspiler.New(type));
il.Emit(FishTranspiler.LocalVariable(variable));
il.Emit(FishTranspiler.Return);
var needsTempVar = type.IsValueType
&& parameters.Length == 0
&& !Array.Exists(type.GetConstructors(AccessTools.allDeclared & ~BindingFlags.Static),
static c => c.GetParameters().Length == 0);
return dm.CreateDelegate(typeof(Func<>).MakeGenericType(type));
var tempVariable = needsTempVar ? FishTranspiler.NewLocalVariable(type, il) : default;
if (needsTempVar)
il.Emit(tempVariable.Load().Address());
for (var i = 0; i < parameters.Length; i++)
il.Emit(FishTranspiler.Argument(i));
il.Emit(FishTranspiler.New(type, parameters));
if (needsTempVar)
il.Emit(tempVariable.Load());
if (delegateReturnType != type)
{
if (type.IsValueType
&& (delegateReturnType == typeof(object)
|| (delegateReturnType.IsInterface && type.IsAssignableTo(delegateReturnType))))
{
il.Emit(FishTranspiler.Box(type));
}
else if (delegateReturnType != typeof(IConvertible)
&& delegateReturnType.IsAssignableTo(typeof(IConvertible))
&& type.IsAssignableTo(typeof(IConvertible)))
{
il.Emit(FishTranspiler.Call(Array.Find(typeof(Convert).GetMethods(),
static m => m.GetParameters().Length == 1 && m.Name == "Type")
.MakeGenericMethod(type, delegateReturnType)));
}
else if (!type.IsAssignableTo(delegateReturnType))
{
il.Emit(FishTranspiler.Cast(delegateReturnType));
}
}
il.Emit(FishTranspiler.Return);
return (T)dm.CreateDelegate(typeof(T));
}
catch (Exception e)
{
Log.Error($"Failed creating a constructor delegate for {type.FullDescription()} with parameters {
parameters.ToStringSafeEnumerable()}\n{e}\n{new StackTrace()}");
return null;
}
}
[SecuritySafeCritical]
public static string FullDescription(this MemberInfo? memberInfo)
=> memberInfo switch
{
MethodBase methodBase => GeneralExtensions.FullDescription(methodBase),
Type type => GeneralExtensions.FullDescription(type),
_ => FullDescriptionInternal(memberInfo)
};
private static string FullDescriptionInternal(MemberInfo? memberInfo)
{
if (memberInfo is null)
return "NULL";
var stringBuilder = new StringBuilder();
switch (memberInfo)
{
case FieldInfo fieldInfo:
AppendFieldPrefix(fieldInfo, stringBuilder);
break;
case PropertyInfo propertyInfo:
AppendPropertyPrefix(propertyInfo, stringBuilder);
break;
case EventInfo eventInfo:
AppendEventPrefix(eventInfo, stringBuilder);
break;
}
if (memberInfo.DeclaringType != null)
stringBuilder.AppendType(memberInfo.DeclaringType).Append("::");
stringBuilder.Append(memberInfo.Name);
if (memberInfo is PropertyInfo propertyInfo1)
AppendPropertyPostfix(stringBuilder, propertyInfo1);
return stringBuilder.ToString();
}
private static void AppendFieldPrefix(FieldInfo fieldInfo, StringBuilder stringBuilder)
{
if (fieldInfo.IsPublic)
stringBuilder.Append("public ");
if (fieldInfo is { IsLiteral: true, IsInitOnly: false })
stringBuilder.Append("const ");
else if (fieldInfo.IsStatic)
stringBuilder.Append("static ");
stringBuilder.AppendType(fieldInfo.FieldType).Append(' ');
}
private static void AppendPropertyPrefix(PropertyInfo propertyInfo, StringBuilder stringBuilder)
{
if ((propertyInfo.GetMethod?.IsPublic ?? false)
|| (propertyInfo.SetMethod?.IsPublic ?? false))
{
stringBuilder.Append("public ");
}
if ((propertyInfo.GetMethod?.IsStatic ?? true)
&& (propertyInfo.SetMethod?.IsStatic ?? true))
{
stringBuilder.Append("static ");
}
stringBuilder.AppendType(propertyInfo.PropertyType).Append(' ');
}
private static void AppendPropertyPostfix(StringBuilder stringBuilder, PropertyInfo propertyInfo)
{
stringBuilder.Append(" { ");
var getMethod = propertyInfo.GetMethod;
var setMethod = propertyInfo.SetMethod;
if (getMethod is not null)
{
if (getMethod.IsPrivate && !(setMethod?.IsPrivate ?? true))
stringBuilder.Append("private ");
stringBuilder.Append("get; ");
}
if (setMethod is not null)
{
if (setMethod.IsPrivate && !(getMethod?.IsPrivate ?? true))
stringBuilder.Append("private ");
stringBuilder.Append("set; ");
}
stringBuilder.Append('}');
}
private static void AppendEventPrefix(EventInfo eventInfo, StringBuilder stringBuilder)
{
if ((eventInfo.AddMethod?.IsPublic ?? true)
&& (eventInfo.RemoveMethod?.IsPublic ?? true))
{
stringBuilder.Append("public ");
}
if ((eventInfo.AddMethod?.IsStatic ?? true)
&& (eventInfo.RemoveMethod?.IsStatic ?? true))
{
stringBuilder.Append("static ");
}
stringBuilder.Append("event ");
stringBuilder.AppendType(eventInfo.EventHandlerType).Append(' ');
}
private static StringBuilder AppendType(this StringBuilder stringBuilder, Type type)
=> stringBuilder.Append(type.FullDescription());
private static T CreateInstanceUsingActivator<T>(params object?[] args)
=> (T)Activator.CreateInstance(typeof(T),
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, null,
args, null, null);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T New<T>()
=> Create<T>.@new();
public static T New<T>() => Create<T>.@new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T_result New<T_result, T_argument>(T_argument argument)
=> Create<T_result, T_argument>.@new(argument);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T_result New<T_result, T1, T2>(T1 arg1, T2 arg2)
=> Create<T_result, T1, T2>.@new(arg1, arg2);
public static T_result New<T_result, T1, T2>(T1 arg1, T2 arg2) => Create<T_result, T1, T2>.@new(arg1, arg2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T_result New<T_result, T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
@@ -203,38 +456,103 @@ public static class Reflection
private static class Create<T>
{
internal static Func<T> @new
= typeof(T).IsValueType ? (Func<T>)MakeStructDefaultConstructor(typeof(T))
: MatchingConstructor(typeof(T), Array.Empty<Type>())!.CreateDelegate<Func<T>>();
internal static volatile Func<T> @new = SimpleFunc;
static Create() => Task.Run(ConstructorTask);
private static T SimpleFunc() => (T)Activator.CreateInstance(typeof(T), true);
private static void ConstructorTask()
{
if (CreateConstructorDelegate<Func<T>>(typeof(T), Array.Empty<Type>()) is { } newFunc)
@new = newFunc;
}
}
private static class Create<T_result, T_argument>
private static class Create<TResult, TArgument>
{
internal static Func<T_argument, T_result> @new
= MatchingConstructor(typeof(T_result), new[] { typeof(T_argument) })!.CreateDelegate<Func<T_argument, T_result>>();
internal static volatile Func<TArgument, TResult> @new = SimpleFunc;
static Create() => Task.Run(ConstructorTask);
private static TResult SimpleFunc(TArgument argument) => CreateInstanceUsingActivator<TResult>(argument);
private static void ConstructorTask()
{
if (CreateConstructorDelegate<Func<TArgument, TResult>>(typeof(TResult), typeof(TArgument)) is { } newFunc)
@new = newFunc;
}
}
private static class Create<T_result, T1, T2>
private static class Create<TResult, T1, T2>
{
internal static Func<T1, T2, T_result> @new
= MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2) })!.CreateDelegate<Func<T1, T2, T_result>>();
internal static volatile Func<T1, T2, TResult> @new = SimpleFunc;
static Create() => Task.Run(ConstructorTask);
private static TResult SimpleFunc(T1 first, T2 second) => CreateInstanceUsingActivator<TResult>(first, second);
private static void ConstructorTask()
{
if (CreateConstructorDelegate<Func<T1, T2, TResult>>(typeof(TResult), typeof(T1), typeof(T2)) is { } newFunc)
@new = newFunc;
}
}
private static class Create<T_result, T1, T2, T3>
private static class Create<TResult, T1, T2, T3>
{
internal static Func<T1, T2, T3, T_result> @new
= MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2), typeof(T3) })!.CreateDelegate<Func<T1, T2, T3, T_result>>();
internal static volatile Func<T1, T2, T3, TResult> @new = SimpleFunc;
static Create() => Task.Run(ConstructorTask);
private static TResult SimpleFunc(T1 first, T2 second, T3 third)
=> CreateInstanceUsingActivator<TResult>(first, second, third);
private static void ConstructorTask()
{
if (CreateConstructorDelegate<Func<T1, T2, T3, TResult>>(typeof(TResult), typeof(T1), typeof(T2), typeof(T3))
is { } newFunc)
{
@new = newFunc;
}
}
}
private static class Create<T_result, T1, T2, T3, T4>
private static class Create<TResult, T1, T2, T3, T4>
{
internal static Func<T1, T2, T3, T4, T_result> @new
= MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) })!.CreateDelegate<Func<T1, T2, T3, T4, T_result>>();
internal static volatile Func<T1, T2, T3, T4, TResult> @new = SimpleFunc;
static Create() => Task.Run(ConstructorTask);
private static TResult SimpleFunc(T1 first, T2 second, T3 third, T4 fourth)
=> CreateInstanceUsingActivator<TResult>(first, second, third, fourth);
private static void ConstructorTask()
{
if (CreateConstructorDelegate<Func<T1, T2, T3, T4, TResult>>(typeof(TResult), typeof(T1), typeof(T2), typeof(T3),
typeof(T4)) is { } newFunc)
{
@new = newFunc;
}
}
}
private static class Create<T_result, T1, T2, T3, T4, T5>
private static class Create<TResult, T1, T2, T3, T4, T5>
{
internal static Func<T1, T2, T3, T4, T5, T_result> @new
= MatchingConstructor(typeof(T_result), new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) })!.CreateDelegate<Func<T1, T2, T3, T4, T5, T_result>>();
internal static volatile Func<T1, T2, T3, T4, T5, TResult> @new = SimpleFunc;
static Create() => Task.Run(ConstructorTask);
private static TResult SimpleFunc(T1 first, T2 second, T3 third, T4 fourth, T5 fifth)
=> CreateInstanceUsingActivator<TResult>(first, second, third, fourth, fifth);
private static void ConstructorTask()
{
if (CreateConstructorDelegate<Func<T1, T2, T3, T4, T5, TResult>>(typeof(TResult), typeof(T1), typeof(T2),
typeof(T3), typeof(T4), typeof(T5)) is { } newFunc)
{
@new = newFunc;
}
}
}
}
+18 -6
View File
@@ -4,8 +4,11 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Collections.Concurrent;
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
public abstract class SingletonFactory<T_base> where T_base : notnull
{
/// <summary>
@@ -13,8 +16,7 @@ public abstract class SingletonFactory<T_base> where T_base : notnull
/// </summary>
/// <typeparam name="T">The type of the instance</typeparam>
/// <returns>The Singleton instance</returns>
public static T Get<T>() where T : T_base
=> (T)GetInternal(typeof(T));
public static T Get<T>() where T : T_base => (T)GetInternal(typeof(T));
/// <summary>
/// Get a Singleton instance of a type derived from T_base
@@ -24,21 +26,31 @@ public abstract class SingletonFactory<T_base> where T_base : notnull
public static T_base Get(Type type)
{
if (!typeof(T_base).IsAssignableFrom(type))
ThrowHelper.ThrowArgumentException($"Parameters for {typeof(T_base).Name}.Get must be assignable from Type {typeof(T_base).Name}. Got {type.FullDescription()} instead.");
{
ThrowHelper.ThrowArgumentException(
$"Parameters for {typeof(T_base).Name}.Get must be assignable from Type {
typeof(T_base).Name}. Got {type.FullDescription()} instead.");
}
return GetInternal(type);
}
/// <summary>
/// protected to allow declaring of default values on derived types. Do not directly invoke this constructor or expose it as public.
/// protected to allow declaring of default values on derived types. Do not directly invoke this constructor or
/// expose it as public.
/// </summary>
protected SingletonFactory()
{
if (_instances.ContainsKey(GetType()))
ThrowHelper.ThrowInvalidOperationException($"Tried creating a second instance of {GetType().Name}. Only the {typeof(T_base).Name}.Get method should be used for instantiation.");
{
ThrowHelper.ThrowInvalidOperationException(
$"Tried creating a second instance of {GetType().Name}. Only the {
typeof(T_base).Name}.Get method should be used for instantiation.");
}
}
private static T_base GetInternal(Type type) => _instances.GetOrAdd(type, type => (T_base)Activator.CreateInstance(type, true));
private static T_base GetInternal(Type type)
=> _instances.GetOrAdd(type, static type => (T_base)Activator.CreateInstance(type, true));
private static readonly ConcurrentDictionary<Type, T_base> _instances = new();
}
+8 -6
View File
@@ -6,6 +6,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
@@ -14,6 +15,7 @@ namespace FisheryLib;
/// </summary>
/// <typeparam name="T">The type of items to enumerate.</typeparam>
[EditorBrowsable(EditorBrowsableState.Never)]
[PublicAPI]
public ref struct SpanEnumerable<T>
{
/// <summary>
@@ -58,12 +60,12 @@ public ref struct SpanEnumerable<T>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
// On .NET Standard 2.1 and .NET Core (or on any target that offers runtime
// support for the Span<T> types), we can save 4 bytes by piggybacking the
// current index in the length of the wrapped span. We're going to use the
// first item as the target reference, and the length as a host for the
// current original offset. This is not possible on eg. .NET Standard 2.0,
// as we lack the API to create Span<T>-s from arbitrary references.
// On .NET Standard 2.1 and .NET Core (or on any target that offers runtime
// support for the Span<T> types), we can save 4 bytes by piggybacking the
// current index in the length of the wrapped span. We're going to use the
// first item as the target reference, and the length as a host for the
// current original offset. This is not possible on eg. .NET Standard 2.0,
// as we lack the API to create Span<T>-s from arbitrary references.
=> new(ref Unsafe.Add(ref _span.DangerousGetPinnableReference(), (nint)(uint)_index), _index);
}
+155 -156
View File
@@ -1,5 +1,5 @@
// Based on
// https://github.com/CommunityToolkit/dotnet/blob/main/CommunityToolkit.HighPerformance/Extensions/SpanExtensions.cs
// https://github.com/CommunityToolkit/dotnet/blob/main/src/CommunityToolkit.HighPerformance/Extensions/SpanExtensions.cs
// with additional methods and support for more types
// Licensed to the .NET Foundation under one or more agreements.
@@ -8,8 +8,11 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
public static class SpanExtensions
{
/// <summary>
@@ -45,87 +48,89 @@ public static class SpanExtensions
=> ArraySortHelper<TKey, TValue>.Default.Sort(keys, values, comparer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T First<T>(this Span<T> span)
=> ref span[0];
public static ref T First<T>(this Span<T> span) => ref span[0];
[return: MaybeNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T FirstOrDefault<T>(this Span<T> span)
=> !span.IsEmpty ? span[0]
: default;
=> !span.IsEmpty
? span[0]
: default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ref TSource First<TSource>(this Span<TSource> span, delegate*<ref TSource, bool> predicate)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (predicate(ref Unsafe.Add(ref r0, offset)))
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
if (predicate(ref Unsafe.Add(ref r0, offset + 1)))
return ref Unsafe.Add(ref r0, offset + 1);
if (predicate(ref Unsafe.Add(ref r0, offset + 2)))
return ref Unsafe.Add(ref r0, offset + 2);
if (predicate(ref Unsafe.Add(ref r0, offset + 3)))
return ref Unsafe.Add(ref r0, offset + 3);
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource))))
return ref Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource));
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)))))
return ref Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)));
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)))))
return ref Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)));
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(TSource);
}
while (length > 0)
{
if (predicate(ref Unsafe.Add(ref r0, offset)))
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
length--;
offset++;
offset += (nuint)sizeof(TSource);
}
return ref ThrowNotFoundException<TSource>();
returnForOffset0:
return ref Unsafe.Add(ref r0, offset);
return ref Unsafe.AddByteOffset(ref r0, offset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TSource First<TSource>(this Span<TSource> span, Predicate<TSource> predicate)
public static unsafe ref TSource First<TSource>(this Span<TSource> span, Predicate<TSource> predicate)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (predicate(Unsafe.Add(ref r0, offset)))
if (predicate(Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
if (predicate(Unsafe.Add(ref r0, offset + 1)))
return ref Unsafe.Add(ref r0, offset + 1);
if (predicate(Unsafe.Add(ref r0, offset + 2)))
return ref Unsafe.Add(ref r0, offset + 2);
if (predicate(Unsafe.Add(ref r0, offset + 3)))
return ref Unsafe.Add(ref r0, offset + 3);
if (predicate(Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource))))
return ref Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource));
if (predicate(Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)))))
return ref Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)));
if (predicate(Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)))))
return ref Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)));
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(TSource);
}
while (length > 0)
{
if (predicate(Unsafe.Add(ref r0, offset)))
if (predicate(Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
length--;
offset++;
offset += (nuint)sizeof(TSource);
}
return ref ThrowNotFoundException<TSource>();
returnForOffset0:
return ref Unsafe.Add(ref r0, offset);
return ref Unsafe.AddByteOffset(ref r0, offset);
}
[DoesNotReturn]
@@ -133,145 +138,143 @@ public static class SpanExtensions
[return: MaybeNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe TSource FirstOrDefault<TSource>(this Span<TSource> span, delegate*<ref TSource, bool> predicate)
public static unsafe TSource FirstOrDefault<TSource>(this Span<TSource> span,
delegate*<ref TSource, bool> predicate)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (predicate(ref Unsafe.Add(ref r0, offset)))
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
if (predicate(ref Unsafe.Add(ref r0, offset + 1)))
return Unsafe.Add(ref r0, offset + 1);
if (predicate(ref Unsafe.Add(ref r0, offset + 2)))
return Unsafe.Add(ref r0, offset + 2);
if (predicate(ref Unsafe.Add(ref r0, offset + 3)))
return Unsafe.Add(ref r0, offset + 3);
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource))))
return Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource));
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)))))
return Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)));
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)))))
return Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)));
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(TSource);
}
while (length > 0)
{
if (predicate(ref Unsafe.Add(ref r0, offset)))
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
length--;
offset++;
offset += (nuint)sizeof(TSource);
}
return default;
returnForOffset0:
return Unsafe.Add(ref r0, offset);
return Unsafe.AddByteOffset(ref r0, offset);
}
[return: MaybeNull]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSource FirstOrDefault<TSource>(this Span<TSource> span, Predicate<TSource> predicate)
public static unsafe TSource FirstOrDefault<TSource>(this Span<TSource> span, Predicate<TSource> predicate)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (predicate(Unsafe.Add(ref r0, offset)))
if (predicate(Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
if (predicate(Unsafe.Add(ref r0, offset + 1)))
return Unsafe.Add(ref r0, offset + 1);
if (predicate(Unsafe.Add(ref r0, offset + 2)))
return Unsafe.Add(ref r0, offset + 2);
if (predicate(Unsafe.Add(ref r0, offset + 3)))
return Unsafe.Add(ref r0, offset + 3);
if (predicate(Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource))))
return Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource));
if (predicate(Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)))))
return Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource)));
if (predicate(Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)))))
return Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)));
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(TSource);
}
while (length > 0)
{
if (predicate(Unsafe.Add(ref r0, offset)))
if (predicate(Unsafe.AddByteOffset(ref r0, offset)))
goto returnForOffset0;
length--;
offset++;
offset += (nuint)sizeof(TSource);
}
return default;
returnForOffset0:
return Unsafe.Add(ref r0, offset);
return Unsafe.AddByteOffset(ref r0, offset);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Any<TSource>(this Span<TSource> span)
=> !span.IsEmpty;
public static bool Any<TSource>(this Span<TSource> span) => !span.IsEmpty;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool Any<TSource>(this Span<TSource> span, delegate*<ref TSource, bool> predicate)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (predicate(ref Unsafe.Add(ref r0, offset))
|| predicate(ref Unsafe.Add(ref r0, offset + 1))
|| predicate(ref Unsafe.Add(ref r0, offset + 2))
|| predicate(ref Unsafe.Add(ref r0, offset + 3)))
{
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset))
|| predicate(ref Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource)))
|| predicate(ref Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource))))
|| predicate(ref Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)))))
return true;
}
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(TSource);
}
while (length > 0)
{
if (predicate(ref Unsafe.Add(ref r0, offset)))
if (predicate(ref Unsafe.AddByteOffset(ref r0, offset)))
return true;
length--;
offset++;
offset += (nuint)sizeof(TSource);
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Any<TSource>(this Span<TSource> span, Predicate<TSource> predicate)
public static unsafe bool Any<TSource>(this Span<TSource> span, Predicate<TSource> predicate)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (predicate(Unsafe.Add(ref r0, offset))
|| predicate(Unsafe.Add(ref r0, offset + 1))
|| predicate(Unsafe.Add(ref r0, offset + 2))
|| predicate(Unsafe.Add(ref r0, offset + 3)))
{
if (predicate(Unsafe.AddByteOffset(ref r0, offset))
|| predicate(Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(TSource)))
|| predicate(Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(TSource))))
|| predicate(Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(TSource)))))
return true;
}
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(TSource);
}
while (length > 0)
{
if (predicate(Unsafe.Add(ref r0, offset)))
if (predicate(Unsafe.AddByteOffset(ref r0, offset)))
return true;
length--;
offset++;
offset += (nuint)sizeof(TSource);
}
return false;
@@ -285,99 +288,97 @@ public static class SpanExtensions
/// <param name="value">The <typeparamref name="T"/> value to look for.</param>
/// <returns>The number of occurrences of <paramref name="value"/> in <paramref name="span"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Count<T>(this Span<T> span, T value)
public static unsafe int Count<T>(this Span<T> span, T value)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint result = 0;
nint offset = 0;
var length = (nuint)span.Length;
nuint result = 0;
nuint offset = 0;
// Main loop with 4 unrolled iterations
while (length >= 4)
{
result += Unsafe.Add(ref r0, offset).Equals<T>(value).ToByte();
result += Unsafe.Add(ref r0, offset + 1).Equals<T>(value).ToByte();
result += Unsafe.Add(ref r0, offset + 2).Equals<T>(value).ToByte();
result += Unsafe.Add(ref r0, offset + 3).Equals<T>(value).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset).Equals<T>(value).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(T)).Equals<T>(value).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(T))).Equals<T>(value).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(T))).Equals<T>(value).ToByte();
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(T);
}
// Iterate over the remaining values and count those that match
while (length > 0)
{
result += Unsafe.Add(ref r0, offset).Equals<T>(value).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset).Equals<T>(value).ToByte();
length--;
offset++;
offset += (nuint)sizeof(T);
}
return (int)result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains<T>(this Span<T> span, T value)
public static unsafe bool Contains<T>(this Span<T> span, T value)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (Unsafe.Add(ref r0, offset).Equals<T>(value)
|| Unsafe.Add(ref r0, offset + 1).Equals<T>(value)
|| Unsafe.Add(ref r0, offset + 2).Equals<T>(value)
|| Unsafe.Add(ref r0, offset + 3).Equals<T>(value))
{
if (Unsafe.AddByteOffset(ref r0, offset).Equals<T>(value)
|| Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(T)).Equals<T>(value)
|| Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(T))).Equals<T>(value)
|| Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(T))).Equals<T>(value))
return true;
}
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(T);
}
while (length > 0)
{
if (Unsafe.Add(ref r0, offset).Equals<T>(value))
if (Unsafe.AddByteOffset(ref r0, offset).Equals<T>(value))
return true;
length--;
offset++;
offset += (nuint)sizeof(T);
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf<T>(this Span<T> span, T value)
public static unsafe int IndexOf<T>(this Span<T> span, T value)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (Unsafe.Add(ref r0, offset).Equals<T>(value))
if (Unsafe.AddByteOffset(ref r0, offset).Equals<T>(value))
return (int)offset;
if (Unsafe.Add(ref r0, offset + 1).Equals<T>(value))
if (Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(T)).Equals<T>(value))
return (int)offset + 1;
if (Unsafe.Add(ref r0, offset + 2).Equals<T>(value))
if (Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(T))).Equals<T>(value))
return (int)offset + 2;
if (Unsafe.Add(ref r0, offset + 3).Equals<T>(value))
if (Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(T))).Equals<T>(value))
return (int)offset + 3;
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(T);
}
while (length > 0)
{
if (Unsafe.Add(ref r0, offset).Equals<T>(value))
if (Unsafe.AddByteOffset(ref r0, offset).Equals<T>(value))
return (int)offset;
length--;
offset++;
offset += (nuint)sizeof(T);
}
return -1;
@@ -416,14 +417,15 @@ public static class SpanReferenceExtensions
/// <param name="reference">The reference to the target item to get the index for.</param>
/// <returns>The index of <paramref name="reference"/> within <paramref name="span"/>, or <c>-1</c>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf<T>(this Span<T> span, ref T reference)
public static unsafe int IndexOf<T>(this Span<T> span, ref T reference)
{
ref var r0 = ref span.DangerousGetPinnableReference();
var byteOffset = Unsafe.ByteOffset(ref r0, ref reference);
var elementOffset = byteOffset / (nint)(uint)Unsafe.SizeOf<T>();
var elementOffset = byteOffset / (nint)(uint)sizeof(T);
return (nuint)elementOffset >= (uint)span.Length ? -1
return (nuint)elementOffset >= (uint)span.Length
? -1
: (int)elementOffset;
}
@@ -435,8 +437,7 @@ public static class SpanReferenceExtensions
/// <param name="reference">The reference to the target item to locate.</param>
/// <returns>true if the reference points towards an element of <see cref="Span{T}"/>; otherwise, false.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains<T>(this Span<T> span, ref T reference)
=> span.IndexOf(ref reference) >= 0;
public static bool Contains<T>(this Span<T> span, ref T reference) => span.IndexOf(ref reference) >= 0;
}
public static class SpanStructExtensions
@@ -449,102 +450,100 @@ public static class SpanStructExtensions
/// <param name="value">The <typeparamref name="T"/> value to look for.</param>
/// <returns>The number of occurrences of <paramref name="value"/> in <paramref name="span"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Count<T>(this Span<T> span, in T value)
public static unsafe int Count<T>(this Span<T> span, in T value)
where T : struct
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint result = 0;
nint offset = 0;
var length = (nuint)span.Length;
nuint result = 0;
nuint offset = 0;
// Main loop with 4 unrolled iterations
while (length >= 4)
{
result += Unsafe.Add(ref r0, offset).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.Add(ref r0, offset + 1).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.Add(ref r0, offset + 2).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.Add(ref r0, offset + 3).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(T)).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(T))).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(T))).Equals(ref Unsafe.AsRef(in value)).ToByte();
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(T);
}
// Iterate over the remaining values and count those that match
while (length > 0)
{
result += Unsafe.Add(ref r0, offset).Equals(ref Unsafe.AsRef(in value)).ToByte();
result += Unsafe.AddByteOffset(ref r0, offset).Equals(ref Unsafe.AsRef(in value)).ToByte();
length--;
offset++;
offset += (nuint)sizeof(T);
}
return (int)result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Contains<T>(this Span<T> span, in T value)
public static unsafe bool Contains<T>(this Span<T> span, in T value)
where T : struct
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (Unsafe.Add(ref r0, offset).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.Add(ref r0, offset + 1).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.Add(ref r0, offset + 2).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.Add(ref r0, offset + 3).Equals(ref Unsafe.AsRef(in value)))
{
if (Unsafe.AddByteOffset(ref r0, offset).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(T)).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(T))).Equals(ref Unsafe.AsRef(in value))
|| Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(T))).Equals(ref Unsafe.AsRef(in value)))
return true;
}
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(T);
}
while (length > 0)
{
if (Unsafe.Add(ref r0, offset).Equals(ref Unsafe.AsRef(in value)))
if (Unsafe.AddByteOffset(ref r0, offset).Equals(ref Unsafe.AsRef(in value)))
return true;
length--;
offset++;
offset += (nuint)sizeof(T);
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOf<T>(this Span<T> span, in T value)
public static unsafe int IndexOf<T>(this Span<T> span, in T value)
where T : struct
{
ref var r0 = ref span.DangerousGetPinnableReference();
var length = (nint)(uint)span.Length;
nint offset = 0;
var length = (nuint)span.Length;
nuint offset = 0;
while (length >= 4)
{
if (Unsafe.Add(ref r0, offset).Equals(ref Unsafe.AsRef(in value)))
if (Unsafe.AddByteOffset(ref r0, offset).Equals(ref Unsafe.AsRef(in value)))
return (int)offset;
if (Unsafe.Add(ref r0, offset + 1).Equals(ref Unsafe.AsRef(in value)))
if (Unsafe.AddByteOffset(ref r0, offset + (nuint)sizeof(T)).Equals(ref Unsafe.AsRef(in value)))
return (int)offset + 1;
if (Unsafe.Add(ref r0, offset + 2).Equals(ref Unsafe.AsRef(in value)))
if (Unsafe.AddByteOffset(ref r0, offset + (2 * (nuint)sizeof(T))).Equals(ref Unsafe.AsRef(in value)))
return (int)offset + 2;
if (Unsafe.Add(ref r0, offset + 3).Equals(ref Unsafe.AsRef(in value)))
if (Unsafe.AddByteOffset(ref r0, offset + (3 * (nuint)sizeof(T))).Equals(ref Unsafe.AsRef(in value)))
return (int)offset + 3;
length -= 4;
offset += 4;
offset += 4 * (nuint)sizeof(T);
}
while (length > 0)
{
if (Unsafe.Add(ref r0, offset).Equals(ref Unsafe.AsRef(in value)))
if (Unsafe.AddByteOffset(ref r0, offset).Equals(ref Unsafe.AsRef(in value)))
return (int)offset;
length--;
offset++;
offset += (nuint)sizeof(T);
}
return -1;
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v.2.0.If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
#pragma warning disable CS9081, CS9080
[PublicAPI]
public unsafe ref struct StackList<T> where T : unmanaged
{
public const int MAX_LENGTH = 1024;
private Span<T> _span;
public int Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private set;
}
public T this[int index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Guard.IsLessThan(index, Count);
return _span[index];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
Guard.IsLessThan(index, Count);
_span[index] = value;
}
}
public void Add(T value)
{
if (++Count >= _span.Length)
Expand();
_span[Count - 1] = value;
}
private void Expand()
{
var newLength = _span.Length * 2;
var newSpan = newLength > MAX_LENGTH ? new T[newLength] : stackalloc T[newLength];
_span.CopyTo(newSpan);
_span = newSpan;
}
public void RemoveAt(int index)
{
Guard.IsLessThan(index, Count);
Count--;
if (index == Count)
return;
_span[index..Count].CopyTo(_span);
}
public T[] ToArray()
{
var array = new T[Count];
_span[..Count].CopyTo(array);
return array;
}
public List<T> ToList()
{
var list = new List<T>(Count);
_span[..Count].CopyTo(list._items);
return list;
}
public StackList() => _span = stackalloc T[4];
public StackList(int capacity) => _span = capacity > MAX_LENGTH ? new T[capacity] : stackalloc T[capacity];
}
#pragma warning restore CS9081
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2022 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v.2.0.If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Diagnostics;
using JetBrains.Annotations;
namespace FisheryLib;
[PublicAPI]
public static class StopwatchExtensions
{
public static decimal ElapsedMilliSecondsAccurate(this Stopwatch stopwatch)
=> stopwatch.ElapsedTicks / (_frequency / 1000m);
public static decimal ElapsedSecondsAccurate(this Stopwatch stopwatch)
=> stopwatch.ElapsedTicks / _frequency;
public static decimal ElapsedMicroSeconds(this Stopwatch stopwatch)
=> stopwatch.ElapsedTicks / (_frequency / 1e6m);
public static decimal ElapsedNanoSeconds(this Stopwatch stopwatch)
=> stopwatch.ElapsedTicks / (_frequency / 1e9m);
private static decimal _frequency = Stopwatch.Frequency;
}
+5 -23
View File
@@ -7,12 +7,14 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace FisheryLib;
internal static class UtilityF
{
internal static void ThrowIfNullOrEmpty<T>(IEnumerable<T> sequence, [CallerArgumentExpression(nameof(sequence))] string argumentName = "")
internal static void ThrowIfNullOrEmpty<T>([NoEnumeration] IEnumerable<T> sequence,
[CallerArgumentExpression(nameof(sequence))] string argumentName = "")
{
Guard.IsNotNull(sequence, argumentName);
@@ -24,7 +26,8 @@ internal static class UtilityF
internal static void Throw(string message) => throw new(message);
[DoesNotReturn]
internal static void Throw<T>(string message) where T : Exception => throw (T)Activator.CreateInstance(typeof(T), message);
internal static void Throw<T>(string message) where T : Exception
=> throw (T)Activator.CreateInstance(typeof(T), message);
[MethodImpl(MethodImplOptions.NoInlining)]
internal static Assembly GetCallingAssembly()
@@ -43,25 +46,4 @@ internal static class UtilityF
return Assembly.GetCallingAssembly();
}
/*internal static T New<T>() => Create<T>.@new();
internal static T New<T, V>(V argument) => Create<T, V>.@new(argument);
internal static T New<T, V, W>(V firstArgument, W secondArgument) => Create<T, V, W>.@new(firstArgument, secondArgument);
private static class Create<T>
{
internal static Func<T> @new = AccessTools.Constructor(typeof(T)).CreateDelegate<Func<T>>();
}
private static class Create<T, V>
{
internal static Func<V, T> @new = AccessTools.Constructor(typeof(T), new[] { typeof(V) }).CreateDelegate<Func<V, T>>();
}
private static class Create<T, V, W>
{
internal static Func<V, W, T> @new = AccessTools.Constructor(typeof(T), new[] { typeof(V), typeof(W) }).CreateDelegate<Func<V, W, T>>();
}*/
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright (c) 2023 bradson
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System.Runtime.CompilerServices;
namespace FisheryLib.Utility;
public static class FishMath
{
public const double PHI
= 1.61803398874989484820458683436563811772030917980576286213544862270526046281890244970720720418939113748475;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int TrailingZeroCount(uint value)
=> value == 0
? 32
: _trailingZeroCountDeBruijn[(int)(((value & unchecked(0 - value)) * 125613361) >> 27)];
public static int NextPowerOfTwo(int value)
{
#if NETCORE
return 1L << (63 - BitOperations.LeadingZeroCount(value));
#else
value--;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value++;
return value;
#endif
}
private static byte[] _trailingZeroCountDeBruijn
= {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22,
20, 15, 25, 17, 4, 8, 31, 27, 13, 23,
21, 19, 16, 7, 26, 12, 18, 6, 11, 5,
10, 9
};
}