diff --git a/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnAttribute.cs deleted file mode 100644 index 5f11632ac18..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NETSTANDARD2_1_OR_GREATER - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Applied to a method that will never return under any circumstance. - /// - /// Internal copy from the BCL attribute. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute - { - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnIfAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnIfAttribute.cs deleted file mode 100644 index 06799235131..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Attributes/DoesNotReturnIfAttribute.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NETSTANDARD2_1_OR_GREATER - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that a given also indicates - /// whether the method will not return (eg. throw an exception). - /// - /// Internal copy from the BCL attribute. - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class DoesNotReturnIfAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The condition parameter value. Code after the method will be considered unreachable - /// by diagnostics if the argument to the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) - { - ParameterValue = parameterValue; - } - - /// - /// Gets a value indicating whether the parameter value should be . - /// - public bool ParameterValue { get; } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Attributes/NotNullAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/NotNullAttribute.cs deleted file mode 100644 index 4595d9d9cd7..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Attributes/NotNullAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NETSTANDARD2_1_OR_GREATER - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that an output will not be even if the corresponding type allows it. - /// Specifies that an input argument was not when the call returns. - /// - /// Internal copy from the BCL attribute. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue)] - internal sealed class NotNullAttribute : Attribute - { - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Attributes/SkipLocalsInitAttribute.cs b/Microsoft.Toolkit.Diagnostics/Attributes/SkipLocalsInitAttribute.cs deleted file mode 100644 index 39d119f6ad2..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Attributes/SkipLocalsInitAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NET5_0 - -namespace System.Runtime.CompilerServices -{ - /// - /// Used to indicate to the compiler that the .locals init flag should not be set in method headers. - /// - /// Internal copy from the BCL attribute. - [AttributeUsage( - AttributeTargets.Module | - AttributeTargets.Class | - AttributeTargets.Struct | - AttributeTargets.Interface | - AttributeTargets.Constructor | - AttributeTargets.Method | - AttributeTargets.Property | - AttributeTargets.Event, - Inherited = false)] - internal sealed class SkipLocalsInitAttribute : Attribute - { - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Extensions/TypeExtensions.cs b/Microsoft.Toolkit.Diagnostics/Extensions/TypeExtensions.cs deleted file mode 100644 index 0791e2c0433..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Extensions/TypeExtensions.cs +++ /dev/null @@ -1,225 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Linq; -#if NETSTANDARD1_4 -using System.Reflection; -#endif -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helpers for working with types. - /// - public static class TypeExtensions - { - /// - /// The mapping of built-in types to their simple representation. - /// - private static readonly IReadOnlyDictionary BuiltInTypesMap = new Dictionary - { - [typeof(bool)] = "bool", - [typeof(byte)] = "byte", - [typeof(sbyte)] = "sbyte", - [typeof(short)] = "short", - [typeof(ushort)] = "ushort", - [typeof(char)] = "char", - [typeof(int)] = "int", - [typeof(uint)] = "uint", - [typeof(float)] = "float", - [typeof(long)] = "long", - [typeof(ulong)] = "ulong", - [typeof(double)] = "double", - [typeof(decimal)] = "decimal", - [typeof(object)] = "object", - [typeof(string)] = "string", - [typeof(void)] = "void" - }; - - /// - /// A thread-safe mapping of precomputed string representation of types. - /// - private static readonly ConditionalWeakTable DisplayNames = new ConditionalWeakTable(); - - /// - /// Returns a simple string representation of a type. - /// - /// The input type. - /// The string representation of . - [Pure] - public static string ToTypeString(this Type type) - { - // Local function to create the formatted string for a given type - static string FormatDisplayString(Type type, int genericTypeOffset, ReadOnlySpan typeArguments) - { - // Primitive types use the keyword name - if (BuiltInTypesMap.TryGetValue(type, out string? typeName)) - { - return typeName!; - } - - // Array types are displayed as Foo[] - if (type.IsArray) - { - var elementType = type.GetElementType()!; - var rank = type.GetArrayRank(); - - return $"{FormatDisplayString(elementType, 0, elementType.GetGenericArguments())}[{new string(',', rank - 1)}]"; - } - - // By checking generic types here we are only interested in specific cases, - // ie. nullable value types or value typles. We have a separate path for custom - // generic types, as we can't rely on this API in that case, as it doesn't show - // a difference between nested types that are themselves generic, or nested simple - // types from a generic declaring type. To deal with that, we need to manually track - // the offset within the array of generic arguments for the whole constructed type. - if (type.IsGenericType()) - { - var genericTypeDefinition = type.GetGenericTypeDefinition(); - - // Nullable types are displayed as T? - if (genericTypeDefinition == typeof(Nullable<>)) - { - var nullableArguments = type.GetGenericArguments(); - - return $"{FormatDisplayString(nullableArguments[0], 0, nullableArguments)}?"; - } - - // ValueTuple 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<,,,,,,,>)) - { - var formattedTypes = type.GetGenericArguments().Select(t => FormatDisplayString(t, 0, t.GetGenericArguments())); - - return $"({string.Join(", ", formattedTypes)})"; - } - } - - string displayName; - - // Generic types - if (type.Name.Contains('`')) - { - // Retrieve the current generic arguments for the current type (leaf or not) - var tokens = type.Name.Split('`'); - 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())); - - // Standard generic types are displayed as Foo - displayName = $"{tokens[0]}<{string.Join(", ", formattedTypes)}>"; - - // Track the current offset for the shared generic arguments list - genericTypeOffset += genericArgumentsCount; - } - else - { - // Simple custom types - displayName = type.Name; - } - - // If the type is nested, recursively format the hierarchy as well - if (type.IsNested) - { - var openDeclaringType = type.DeclaringType!; - var rootGenericArguments = typeArguments.Slice(0, typeArguments.Length - genericTypeOffset).ToArray(); - - // If the declaring type is generic, we need to reconstruct the closed type - // manually, as the declaring type instance doesn't retain type information. - if (rootGenericArguments.Length > 0) - { - var closedDeclaringType = openDeclaringType.GetGenericTypeDefinition().MakeGenericType(rootGenericArguments); - - return $"{FormatDisplayString(closedDeclaringType, genericTypeOffset, typeArguments)}.{displayName}"; - } - - return $"{FormatDisplayString(openDeclaringType, genericTypeOffset, typeArguments)}.{displayName}"; - } - - return $"{type.Namespace}.{displayName}"; - } - - // Atomically get or build the display string for the current type. - return DisplayNames.GetValue(type, t => - { - // By-ref types are displayed as T& - if (t.IsByRef) - { - t = t.GetElementType()!; - - return $"{FormatDisplayString(t, 0, t.GetGenericArguments())}&"; - } - - // Pointer types are displayed as T* - if (t.IsPointer) - { - int depth = 0; - - // Calculate the pointer indirection level - while (t.IsPointer) - { - depth++; - t = t.GetElementType()!; - } - - return $"{FormatDisplayString(t, 0, t.GetGenericArguments())}{new string('*', depth)}"; - } - - // Standard path for concrete types - return FormatDisplayString(t, 0, t.GetGenericArguments()); - }); - } - - /// - /// Returns whether or not a given type is generic. - /// - /// The input type. - /// Whether or not the input type is generic. - [Pure] - private static bool IsGenericType(this Type type) - { -#if NETSTANDARD1_4 - return type.GetTypeInfo().IsGenericType; -#else - return type.IsGenericType; -#endif - } - -#if NETSTANDARD1_4 - /// - /// Returns an array of types representing the generic arguments. - /// - /// The input type. - /// An array of types representing the generic arguments. - [Pure] - private static Type[] GetGenericArguments(this Type type) - { - return type.GetTypeInfo().GenericTypeParameters; - } - - /// - /// Returns whether is an instance of . - /// - /// The input type. - /// The type to check against. - /// if is an instance of , otherwise. - [Pure] - internal static bool IsInstanceOfType(this Type type, object value) - { - return type.GetTypeInfo().IsAssignableFrom(value.GetType().GetTypeInfo()); - } -#endif - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Extensions/ValueTypeExtensions.cs b/Microsoft.Toolkit.Diagnostics/Extensions/ValueTypeExtensions.cs deleted file mode 100644 index 8f1e6fbe97a..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Extensions/ValueTypeExtensions.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helpers for working with value types. - /// - public static class ValueTypeExtensions - { - /// - /// Gets the table of hex characters (doesn't allocate, maps to .text section, see ). - /// - private static ReadOnlySpan 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' - }; - - /// - /// Returns a hexadecimal representation of a given value, left-padded and ordered as big-endian. - /// - /// The input type to format to . - /// The input value to format to . - /// - /// The hexadecimal representation of (with the '0x' prefix), left-padded to byte boundaries and ordered as big-endian. - /// - /// - /// As a byte (8 bits) is represented by two hexadecimal digits (each representing a group of 4 bytes), each - /// representation will always contain an even number of digits. For instance: - /// - /// Console.WriteLine(1.ToHexString()); // "0x01" - /// Console.WriteLine(((byte)255).ToHexString()); // "0xFF" - /// Console.WriteLine((-1).ToHexString()); // "0xFFFFFFFF" - /// - /// - [Pure] - [SkipLocalsInit] - public static unsafe string ToHexString(this T value) - where T : unmanaged - { - int - sizeOfT = Unsafe.SizeOf(), - bufferSize = (2 * sizeOfT) + 2; - char* p = stackalloc char[bufferSize]; - - p[0] = '0'; - p[1] = 'x'; - - ref byte rh = ref MemoryMarshal.GetReference(HexCharactersTable); - - for (int i = 0, j = bufferSize - 2; i < sizeOfT; i++, j -= 2) - { - byte b = ((byte*)&value)[i]; - int - low = b & 0x0F, - high = (b & 0xF0) >> 4; - - p[j + 1] = (char)Unsafe.Add(ref rh, low); - p[j] = (char)Unsafe.Add(ref rh, high); - } - - return new string(p, 0, bufferSize); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.g.cs b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.g.cs deleted file mode 100644 index 454bc3b7d70..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.g.cs +++ /dev/null @@ -1,1827 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// ===================== -// Auto generated file -// ===================== - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Asserts that the input instance must be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(Span span, string name) - { - if (span.Length == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(span, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(Span span, string name) - { - if (span.Length != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmptyWithSpan(name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(Span span, int size, string name) - { - if (span.Length == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(span, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(Span span, int size, string name) - { - if (span.Length != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(span, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(Span span, int size, string name) - { - if (span.Length > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(span, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(Span span, int size, string name) - { - if (span.Length >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(span, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(Span span, int size, string name) - { - if (span.Length < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(span, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(Span span, int size, string name) - { - if (span.Length <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(span, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(Span source, Span destination, string name) - { - if (source.Length == destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(Span source, Span destination, string name) - { - if (source.Length <= destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, destination, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, Span span, string name) - { - if ((uint)index < (uint)span.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, span, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, Span span, string name) - { - if ((uint)index >= (uint)span.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, span, name); - } - - /// - /// Asserts that the input instance must be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(ReadOnlySpan span, string name) - { - if (span.Length == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(span, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(ReadOnlySpan span, string name) - { - if (span.Length != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmptyWithReadOnlySpan(name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(ReadOnlySpan span, int size, string name) - { - if (span.Length == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(span, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(ReadOnlySpan span, int size, string name) - { - if (span.Length != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(span, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(ReadOnlySpan span, int size, string name) - { - if (span.Length > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(span, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(ReadOnlySpan span, int size, string name) - { - if (span.Length >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(span, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(ReadOnlySpan span, int size, string name) - { - if (span.Length < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(span, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(ReadOnlySpan span, int size, string name) - { - if (span.Length <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(span, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(ReadOnlySpan source, Span destination, string name) - { - if (source.Length == destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(ReadOnlySpan source, Span destination, string name) - { - if (source.Length <= destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, destination, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, ReadOnlySpan span, string name) - { - if ((uint)index < (uint)span.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, span, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, ReadOnlySpan span, string name) - { - if ((uint)index >= (uint)span.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, span, name); - } - - /// - /// Asserts that the input instance must be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(Memory memory, string name) - { - if (memory.Length == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(memory, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(Memory memory, string name) - { - if (memory.Length != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty>(name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(Memory memory, int size, string name) - { - if (memory.Length == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(Memory memory, int size, string name) - { - if (memory.Length != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(Memory memory, int size, string name) - { - if (memory.Length > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(Memory memory, int size, string name) - { - if (memory.Length >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(Memory memory, int size, string name) - { - if (memory.Length < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(Memory memory, int size, string name) - { - if (memory.Length <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(memory, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(Memory source, Memory destination, string name) - { - if (source.Length == destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(Memory source, Memory destination, string name) - { - if (source.Length <= destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, destination, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, Memory memory, string name) - { - if ((uint)index < (uint)memory.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, memory, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, Memory memory, string name) - { - if ((uint)index >= (uint)memory.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, memory, name); - } - - /// - /// Asserts that the input instance must be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(ReadOnlyMemory memory, string name) - { - if (memory.Length == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(memory, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(ReadOnlyMemory memory, string name) - { - if (memory.Length != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty>(name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(ReadOnlyMemory memory, int size, string name) - { - if (memory.Length == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(ReadOnlyMemory memory, int size, string name) - { - if (memory.Length != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(ReadOnlyMemory memory, int size, string name) - { - if (memory.Length > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(ReadOnlyMemory memory, int size, string name) - { - if (memory.Length >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(ReadOnlyMemory memory, int size, string name) - { - if (memory.Length < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(memory, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(ReadOnlyMemory memory, int size, string name) - { - if (memory.Length <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(memory, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(ReadOnlyMemory source, Memory destination, string name) - { - if (source.Length == destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(ReadOnlyMemory source, Memory destination, string name) - { - if (source.Length <= destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, destination, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, ReadOnlyMemory memory, string name) - { - if ((uint)index < (uint)memory.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, memory, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, ReadOnlyMemory memory, string name) - { - if ((uint)index >= (uint)memory.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, memory, name); - } - - /// - /// Asserts that the input array instance must be empty. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(T[] array, string name) - { - if (array.Length == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(array, name); - } - - /// - /// Asserts that the input array instance must not be empty. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(T[] array, string name) - { - if (array.Length != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty(name); - } - - /// - /// Asserts that the input array instance must have a size of a specified value. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(T[] array, int size, string name) - { - if (array.Length == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(array, size, name); - } - - /// - /// Asserts that the input array instance must have a size not equal to a specified value. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(T[] array, int size, string name) - { - if (array.Length != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(array, size, name); - } - - /// - /// Asserts that the input array instance must have a size over a specified value. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(T[] array, int size, string name) - { - if (array.Length > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(array, size, name); - } - - /// - /// Asserts that the input array instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(T[] array, int size, string name) - { - if (array.Length >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(array, size, name); - } - - /// - /// Asserts that the input array instance must have a size of less than a specified value. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(T[] array, int size, string name) - { - if (array.Length < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(array, size, name); - } - - /// - /// Asserts that the input array instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input array instance. - /// The input array instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(T[] array, int size, string name) - { - if (array.Length <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(array, size, name); - } - - /// - /// Asserts that the source array instance must have the same size of a destination array instance. - /// - /// The item of items in the input array instance. - /// The source array instance to check the size for. - /// The destination array instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(T[] source, T[] destination, string name) - { - if (source.Length == destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination, name); - } - - /// - /// Asserts that the source array instance must have a size of less than or equal to that of a destination array instance. - /// - /// The item of items in the input array instance. - /// The source array instance to check the size for. - /// The destination array instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(T[] source, T[] destination, string name) - { - if (source.Length <= destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, destination, name); - } - - /// - /// Asserts that the input index is valid for a given array instance. - /// - /// The item of items in the input array instance. - /// The input index to be used to access . - /// The input array instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, T[] array, string name) - { - if ((uint)index < (uint)array.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, array, name); - } - - /// - /// Asserts that the input index is not valid for a given array instance. - /// - /// The item of items in the input array instance. - /// The input index to be used to access . - /// The input array instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, T[] array, string name) - { - if ((uint)index >= (uint)array.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, array, name); - } - - /// - /// Asserts that the input instance must be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(List list, string name) - { - if (list.Count == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty((ICollection)list, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(List list, string name) - { - if (list.Count != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty>(name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(List list, int size, string name) - { - if (list.Count == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo((ICollection)list, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(List list, int size, string name) - { - if (list.Count != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo((ICollection)list, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(List list, int size, string name) - { - if (list.Count > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan((ICollection)list, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(List list, int size, string name) - { - if (list.Count >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo((ICollection)list, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(List list, int size, string name) - { - if (list.Count < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan((ICollection)list, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(List list, int size, string name) - { - if (list.Count <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo((ICollection)list, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(List source, List destination, string name) - { - if (source.Count == destination.Count) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo((ICollection)source, destination.Count, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(List source, List destination, string name) - { - if (source.Count <= destination.Count) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo((ICollection)source, destination.Count, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, List list, string name) - { - if ((uint)index < (uint)list.Count) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, (ICollection)list, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, List list, string name) - { - if ((uint)index >= (uint)list.Count) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, (ICollection)list, name); - } - - /// - /// Asserts that the input instance must be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(ICollection collection, string name) - { - if (collection.Count == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(collection, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(ICollection collection, string name) - { - if (collection.Count != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty>(name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(ICollection collection, int size, string name) - { - if (collection.Count == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(ICollection collection, int size, string name) - { - if (collection.Count != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(ICollection collection, int size, string name) - { - if (collection.Count > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(ICollection collection, int size, string name) - { - if (collection.Count >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(ICollection collection, int size, string name) - { - if (collection.Count < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(ICollection collection, int size, string name) - { - if (collection.Count <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(collection, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(ICollection source, ICollection destination, string name) - { - if (source.Count == destination.Count) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination.Count, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(ICollection source, ICollection destination, string name) - { - if (source.Count <= destination.Count) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination.Count, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, ICollection collection, string name) - { - if ((uint)index < (uint)collection.Count) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, collection, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, ICollection collection, string name) - { - if ((uint)index >= (uint)collection.Count) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, collection, name); - } - - /// - /// Asserts that the input instance must be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(IReadOnlyCollection collection, string name) - { - if (collection.Count == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(collection, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(IReadOnlyCollection collection, string name) - { - if (collection.Count != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty>(name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(IReadOnlyCollection collection, int size, string name) - { - if (collection.Count == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(IReadOnlyCollection collection, int size, string name) - { - if (collection.Count != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(IReadOnlyCollection collection, int size, string name) - { - if (collection.Count > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(IReadOnlyCollection collection, int size, string name) - { - if (collection.Count >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(IReadOnlyCollection collection, int size, string name) - { - if (collection.Count < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(collection, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input instance. - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(IReadOnlyCollection collection, int size, string name) - { - if (collection.Count <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(collection, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(IReadOnlyCollection source, ICollection destination, string name) - { - if (source.Count == destination.Count) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination.Count, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The item of items in the input instance. - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(IReadOnlyCollection source, ICollection destination, string name) - { - if (source.Count <= destination.Count) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination.Count, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, IReadOnlyCollection collection, string name) - { - if ((uint)index < (uint)collection.Count) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, collection, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The item of items in the input instance. - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, IReadOnlyCollection collection, string name) - { - if ((uint)index >= (uint)collection.Count) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, collection, name); - } - } -} diff --git a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.tt b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.tt deleted file mode 100644 index 22bf00947e7..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Collection.tt +++ /dev/null @@ -1,301 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -<#@include file="TypeInfo.ttinclude" #> -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { -<# -GenerateTextForItems(EnumerableTypes, item => -{ -#> - /// - /// Asserts that the input <#=item.XmlType#> instance must be empty. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(<#=item.Type#> <#=item.Name#>, string name) - { - if (<#=item.Name#>.<#=item.Size#> == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(<#=item.Cast#><#=item.Name#>, name); - } - - /// - /// Asserts that the input <#=item.XmlType#> instance must not be empty. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is == 0. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(<#=item.Type#> <#=item.Name#>, string name) - { - if (<#=item.Name#>.<#=item.Size#> != 0) - { - return; - } - -<# - if (item.Type == "Span") - { -#> - ThrowHelper.ThrowArgumentExceptionForIsNotEmptyWithSpan(name); -<# - } - else if (item.Type == "ReadOnlySpan") - { -#> - ThrowHelper.ThrowArgumentExceptionForIsNotEmptyWithReadOnlySpan(name); -<# - } - else - { -#> - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty<<#=item.Type#>>(name); -<# - } -#> - } - - /// - /// Asserts that the input <#=item.XmlType#> instance must have a size of a specified value. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(<#=item.Type#> <#=item.Name#>, int size, string name) - { - if (<#=item.Name#>.<#=item.Size#> == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#=item.Cast#><#=item.Name#>, size, name); - } - - /// - /// Asserts that the input <#=item.XmlType#> instance must have a size not equal to a specified value. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(<#=item.Type#> <#=item.Name#>, int size, string name) - { - if (<#=item.Name#>.<#=item.Size#> != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(<#=item.Cast#><#=item.Name#>, size, name); - } - - /// - /// Asserts that the input <#=item.XmlType#> instance must have a size over a specified value. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(<#=item.Type#> <#=item.Name#>, int size, string name) - { - if (<#=item.Name#>.<#=item.Size#> > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(<#=item.Cast#><#=item.Name#>, size, name); - } - - /// - /// Asserts that the input <#=item.XmlType#> instance must have a size of at least or equal to a specified value. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(<#=item.Type#> <#=item.Name#>, int size, string name) - { - if (<#=item.Name#>.<#=item.Size#> >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(<#=item.Cast#><#=item.Name#>, size, name); - } - - /// - /// Asserts that the input <#=item.XmlType#> instance must have a size of less than a specified value. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(<#=item.Type#> <#=item.Name#>, int size, string name) - { - if (<#=item.Name#>.<#=item.Size#> < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(<#=item.Cast#><#=item.Name#>, size, name); - } - - /// - /// Asserts that the input <#=item.XmlType#> instance must have a size of less than or equal to a specified value. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input <#=item.XmlType#> instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(<#=item.Type#> <#=item.Name#>, int size, string name) - { - if (<#=item.Name#>.<#=item.Size#> <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(<#=item.Cast#><#=item.Name#>, size, name); - } - - /// - /// Asserts that the source <#=item.XmlType#> instance must have the same size of a destination <#=item.XmlType#> instance. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The source <#=item.XmlType#> instance to check the size for. - /// The destination <#=item.XmlType#> instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(<#=item.Type#> source, <#=item.DestinationType#> destination, string name) - { - if (source.<#=item.Size#> == destination.<#=item.Size#>) - { - return; - } - -<# - if (item.HasCountProperty) - { -#> - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#=item.Cast#>source, destination.<#=item.Size#>, name); -<# - } - else - { -#> - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, <#=item.Cast#>destination, name); -<# - } -#> - } - - /// - /// Asserts that the source <#=item.XmlType#> instance must have a size of less than or equal to that of a destination <#=item.XmlType#> instance. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The source <#=item.XmlType#> instance to check the size for. - /// The destination <#=item.XmlType#> instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(<#=item.Type#> source, <#=item.DestinationType#> destination, string name) - { - if (source.<#=item.Size#> <= destination.<#=item.Size#>) - { - return; - } - -<# - if (item.HasCountProperty) - { -#> - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(<#=item.Cast#>source, destination.<#=item.Size#>, name); -<# - } - else - { -#> - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, <#=item.Cast#>destination, name); -<# - } -#> - } - - /// - /// Asserts that the input index is valid for a given <#=item.XmlType#> instance. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input index to be used to access . - /// The input <#=item.XmlType#> instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, <#=item.Type#> <#=item.Name#>, 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 -#> - if ((uint)index < (uint)<#=item.Name#>.<#=item.Size#>) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, <#=item.Cast#><#=item.Name#>, name); - } - - /// - /// Asserts that the input index is not valid for a given <#=item.XmlType#> instance. - /// - /// The item of items in the input <#=item.XmlType#> instance. - /// The input index to be used to access . - /// The input <#=item.XmlType#> instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, <#=item.Type#> <#=item.Name#>, string name) - { - if ((uint)index >= (uint)<#=item.Name#>.<#=item.Size#>) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, <#=item.Cast#><#=item.Name#>, name); - } -<# -}); -#> - } -} diff --git a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.g.cs b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.g.cs deleted file mode 100644 index 953f007578b..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.g.cs +++ /dev/null @@ -1,3532 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// ===================== -// Auto generated file -// ===================== - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(byte value, byte target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(byte value, byte target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(byte value, byte maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(byte value, byte maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(byte value, byte minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(byte value, byte minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(byte value, byte minimum, byte maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(byte value, byte minimum, byte maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(byte value, byte minimum, byte maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(byte value, byte minimum, byte maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(byte value, byte minimum, byte maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(byte value, byte minimum, byte maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(sbyte value, sbyte target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(sbyte value, sbyte target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(sbyte value, sbyte maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(sbyte value, sbyte maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(sbyte value, sbyte minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(sbyte value, sbyte minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(sbyte value, sbyte minimum, sbyte maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(sbyte value, sbyte minimum, sbyte maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(sbyte value, sbyte minimum, sbyte maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(sbyte value, sbyte minimum, sbyte maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(sbyte value, sbyte minimum, sbyte maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(sbyte value, sbyte minimum, sbyte maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(short value, short target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(short value, short target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(short value, short maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(short value, short maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(short value, short minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(short value, short minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(short value, short minimum, short maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(short value, short minimum, short maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(short value, short minimum, short maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(short value, short minimum, short maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(short value, short minimum, short maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(short value, short minimum, short maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(ushort value, ushort target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(ushort value, ushort target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(ushort value, ushort maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(ushort value, ushort maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(ushort value, ushort minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(ushort value, ushort minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(ushort value, ushort minimum, ushort maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(ushort value, ushort minimum, ushort maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(ushort value, ushort minimum, ushort maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(ushort value, ushort minimum, ushort maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(ushort value, ushort minimum, ushort maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(ushort value, ushort minimum, ushort maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(char value, char target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(char value, char target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(char value, char maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(char value, char maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(char value, char minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(char value, char minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(char value, char minimum, char maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(char value, char minimum, char maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(char value, char minimum, char maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(char value, char minimum, char maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(char value, char minimum, char maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(char value, char minimum, char maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(int value, int target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(int value, int target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(int value, int maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(int value, int maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(int value, int minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(int value, int minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(int value, int minimum, int maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(int value, int minimum, int maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(int value, int minimum, int maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(int value, int minimum, int maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(int value, int minimum, int maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(int value, int minimum, int maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(uint value, uint target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(uint value, uint target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(uint value, uint maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(uint value, uint maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(uint value, uint minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(uint value, uint minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(uint value, uint minimum, uint maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(uint value, uint minimum, uint maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(uint value, uint minimum, uint maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(uint value, uint minimum, uint maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(uint value, uint minimum, uint maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(uint value, uint minimum, uint maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(float value, float target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(float value, float target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(float value, float maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(float value, float maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(float value, float minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(float value, float minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(float value, float minimum, float maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(float value, float minimum, float maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(float value, float minimum, float maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(float value, float minimum, float maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(float value, float minimum, float maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(float value, float minimum, float maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(long value, long target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(long value, long target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(long value, long maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(long value, long maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(long value, long minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(long value, long minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(long value, long minimum, long maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(long value, long minimum, long maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(long value, long minimum, long maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(long value, long minimum, long maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(long value, long minimum, long maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(long value, long minimum, long maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(ulong value, ulong target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(ulong value, ulong target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(ulong value, ulong maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(ulong value, ulong maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(ulong value, ulong minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(ulong value, ulong minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(ulong value, ulong minimum, ulong maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(ulong value, ulong minimum, ulong maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(ulong value, ulong minimum, ulong maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(ulong value, ulong minimum, ulong maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(ulong value, ulong minimum, ulong maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(ulong value, ulong minimum, ulong maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(double value, double target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(double value, double target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(double value, double maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(double value, double maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(double value, double minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(double value, double minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(double value, double minimum, double maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(double value, double minimum, double maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(double value, double minimum, double maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(double value, double minimum, double maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(double value, double minimum, double maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(double value, double minimum, double maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(decimal value, decimal target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(decimal value, decimal target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(decimal value, decimal maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(decimal value, decimal maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(decimal value, decimal minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(decimal value, decimal minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(decimal value, decimal minimum, decimal maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(decimal value, decimal minimum, decimal maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(decimal value, decimal minimum, decimal maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(decimal value, decimal minimum, decimal maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(decimal value, decimal minimum, decimal maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(decimal value, decimal minimum, decimal maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(nint value, nint target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(nint value, nint target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(nint value, nint maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(nint value, nint maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(nint value, nint minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(nint value, nint minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(nint value, nint minimum, nint maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(nint value, nint minimum, nint maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(nint value, nint minimum, nint maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(nint value, nint minimum, nint maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(nint value, nint minimum, nint maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(nint value, nint minimum, nint maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(nuint value, nuint target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(nuint value, nuint target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(nuint value, nuint maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(nuint value, nuint maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(nuint value, nuint minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(nuint value, nuint minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(nuint value, nuint minimum, nuint maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(nuint value, nuint minimum, nuint maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(nuint value, nuint minimum, nuint maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(nuint value, nuint minimum, nuint maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(nuint value, nuint minimum, nuint maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(nuint value, nuint minimum, nuint maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - } -} diff --git a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.tt b/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.tt deleted file mode 100644 index 3f5f0032b06..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/Guard.Comparable.Numeric.tt +++ /dev/null @@ -1,274 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -<#@include file="TypeInfo.ttinclude" #> -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { -<# -GenerateTextForItems(NumericTypes, typeInfo => -{ - var (type, prefix) = typeInfo; -#> - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The input ="<#=type#>"/> value to test. - /// The target ="<#=type#>"/> value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(<#=type#> value, <#=type#> target, string name) - { - if (value == target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The input ="<#=type#>"/> value to test. - /// The target ="<#=type#>"/> value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(<#=type#> value, <#=type#> target, string name) - { - if (value != target) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The input ="<#=type#>"/> value to test. - /// The exclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(<#=type#> value, <#=type#> maximum, string name) - { - if (value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The input ="<#=type#>"/> value to test. - /// The inclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(<#=type#> value, <#=type#> maximum, string name) - { - if (value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The input ="<#=type#>"/> value to test. - /// The exclusive minimum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(<#=type#> value, <#=type#> minimum, string name) - { - if (value > minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The input ="<#=type#>"/> value to test. - /// The inclusive minimum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(<#=type#> value, <#=type#> minimum, string name) - { - if (value >= minimum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The input ="<#=type#>"/> value to test. - /// The inclusive minimum ="<#=type#>"/> value that is accepted. - /// The exclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(<#=type#> value, <#=type#> minimum, <#=type#> maximum, string name) - { - if (value >= minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The input ="<#=type#>"/> value to test. - /// The inclusive minimum ="<#=type#>"/> value that is accepted. - /// The exclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(<#=type#> value, <#=type#> minimum, <#=type#> maximum, string name) - { - if (value < minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input ="<#=type#>"/> value to test. - /// The exclusive minimum ="<#=type#>"/> value that is accepted. - /// The exclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(<#=type#> value, <#=type#> minimum, <#=type#> maximum, string name) - { - if (value > minimum && value < maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input ="<#=type#>"/> value to test. - /// The exclusive minimum ="<#=type#>"/> value that is accepted. - /// The exclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(<#=type#> value, <#=type#> minimum, <#=type#> maximum, string name) - { - if (value <= minimum || value >= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The input ="<#=type#>"/> value to test. - /// The inclusive minimum ="<#=type#>"/> value that is accepted. - /// The inclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(<#=type#> value, <#=type#> minimum, <#=type#> maximum, string name) - { - if (value >= minimum && value <= maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The input ="<#=type#>"/> value to test. - /// The inclusive minimum ="<#=type#>"/> value that is accepted. - /// The inclusive maximum ="<#=type#>"/> value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(<#=type#> value, <#=type#> minimum, <#=type#> maximum, string name) - { - if (value < minimum || value > maximum) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } -<# -}); -#> - } -} diff --git a/Microsoft.Toolkit.Diagnostics/Generated/Guard.md b/Microsoft.Toolkit.Diagnostics/Generated/Guard.md deleted file mode 100644 index 6b381a62b90..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/Guard.md +++ /dev/null @@ -1,17 +0,0 @@ -# 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/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2019)). - -## Why is this needed? - -There are a few reasons why T4 templates are used for the `Guard` class: - - - Especially when a large number of similar overloads are available for the same APIs, 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 code in 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` as type constraint. - - In some cases, using generic methods just isn't possible. For instance, types like `Span` and `ReadOnlySpan` 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. - -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. diff --git a/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.g.cs b/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.g.cs deleted file mode 100644 index da2f40d7356..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.g.cs +++ /dev/null @@ -1,817 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// ===================== -// Auto generated file -// ===================== - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(Span span, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must be empty, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(Span span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(Span span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(Span span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size over {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(Span span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(Span span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(Span span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(Span source, Span destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(Span source, Span destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(Span).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, Span span, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(span.Length)} to be a valid index for the target collection ({typeof(Span).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, Span span, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(span.Length)} to be an invalid index for the target collection ({typeof(Span).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(ReadOnlySpan span, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must be empty, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(ReadOnlySpan span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(ReadOnlySpan span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(ReadOnlySpan span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size over {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(ReadOnlySpan span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(ReadOnlySpan span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(ReadOnlySpan span, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(span.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(ReadOnlySpan source, Span destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(ReadOnlySpan source, Span destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, ReadOnlySpan span, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(span.Length)} to be a valid index for the target collection ({typeof(ReadOnlySpan).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, ReadOnlySpan span, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(span.Length)} to be an invalid index for the target collection ({typeof(ReadOnlySpan).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(Memory memory, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Memory).ToTypeString()}) must be empty, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(Memory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(Memory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(Memory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size over {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(Memory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(Memory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(Memory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(Memory source, Memory destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(Memory source, Memory destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(Memory).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, Memory memory, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(memory.Length)} to be a valid index for the target collection ({typeof(Memory).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, Memory memory, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(memory.Length)} to be an invalid index for the target collection ({typeof(Memory).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(ReadOnlyMemory memory, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must be empty, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(ReadOnlyMemory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(ReadOnlyMemory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(ReadOnlyMemory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size over {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(ReadOnlyMemory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(ReadOnlyMemory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(ReadOnlyMemory memory, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(memory.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(ReadOnlyMemory source, Memory destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(ReadOnlyMemory source, Memory destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(ReadOnlyMemory).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, ReadOnlyMemory memory, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(memory.Length)} to be a valid index for the target collection ({typeof(ReadOnlyMemory).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, ReadOnlyMemory memory, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(memory.Length)} to be an invalid index for the target collection ({typeof(ReadOnlyMemory).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(T[] array, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T[]).ToTypeString()}) must be empty, had a size of {AssertString(array.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(T[] array, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(array.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(T[] array, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(array.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(T[] array, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size over {size}, had a size of {AssertString(array.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(T[] array, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(array.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(T[] array, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(array.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(T[] array, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(array.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(T[] source, T[] destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(T[] source, T[] destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(T[]).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Length)} (the destination), had a size of {AssertString(source.Length)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, T[] array, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(array.Length)} to be a valid index for the target collection ({typeof(T[]).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, T[] array, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(array.Length)} to be an invalid index for the target collection ({typeof(T[]).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(List list, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(List).ToTypeString()}) must be empty, had a size of {AssertString(list.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(List list, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(list.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(List list, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(list.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(List list, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size over {size}, had a size of {AssertString(list.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(List list, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(list.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(List list, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(list.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(List list, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(list.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(List source, List destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size equal to {AssertString(destination.Count)} (the destination), had a size of {AssertString(source.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(List source, List destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(List).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Count)} (the destination), had a size of {AssertString(source.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, List list, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(list.Count)} to be a valid index for the target collection ({typeof(List).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, List list, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(list.Count)} to be an invalid index for the target collection ({typeof(List).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(ICollection collection, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must be empty, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(ICollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(ICollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(ICollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size over {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(ICollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(ICollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(ICollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(ICollection source, ICollection destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size equal to {AssertString(destination.Count)} (the destination), had a size of {AssertString(source.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(ICollection source, ICollection destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(ICollection).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Count)} (the destination), had a size of {AssertString(source.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, ICollection collection, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(collection.Count)} to be a valid index for the target collection ({typeof(ICollection).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, ICollection collection, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(collection.Count)} to be an invalid index for the target collection ({typeof(ICollection).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(IReadOnlyCollection collection, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must be empty, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(IReadOnlyCollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size equal to {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(IReadOnlyCollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size not equal to {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(IReadOnlyCollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size over {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(IReadOnlyCollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size of at least {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(IReadOnlyCollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size less than {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(IReadOnlyCollection collection, int size, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size less than or equal to {size}, had a size of {AssertString(collection.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(IReadOnlyCollection source, ICollection destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size equal to {AssertString(destination.Count)} (the destination), had a size of {AssertString(source.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(IReadOnlyCollection source, ICollection destination, string name) - { - throw new ArgumentException($"The source {AssertString(name)} ({typeof(IReadOnlyCollection).ToTypeString()}) must have a size less than or equal to {AssertString(destination.Count)} (the destination), had a size of {AssertString(source.Count)}", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(int index, IReadOnlyCollection collection, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must be in the range given by <0> and {AssertString(collection.Count)} to be a valid index for the target collection ({typeof(IReadOnlyCollection).ToTypeString()}), was {AssertString(index)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(int index, IReadOnlyCollection collection, string name) - { - throw new ArgumentOutOfRangeException(name, index, $"Parameter {AssertString(name)} (int) must not be in the range given by <0> and {AssertString(collection.Count)} to be an invalid index for the target collection ({typeof(IReadOnlyCollection).ToTypeString()}), was {AssertString(index)}"); - } - } - } -} diff --git a/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.tt b/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.tt deleted file mode 100644 index 9df173d5807..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/ThrowHelper.Collection.tt +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -<#@include file="TypeInfo.ttinclude" #> -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { -<# -GenerateTextForItems(EnumerableTypes, item => -{ -#> - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeNotEqualTo(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThan(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThan(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeEqualTo(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(<#=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); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRangeFor(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)}"); - } - - /// - /// Throws an when (or an overload) fails. - /// - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(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)}"); - } -<# -}); -#> - } - } -} diff --git a/Microsoft.Toolkit.Diagnostics/Generated/TypeInfo.ttinclude b/Microsoft.Toolkit.Diagnostics/Generated/TypeInfo.ttinclude deleted file mode 100644 index b2421dc2a19..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Generated/TypeInfo.ttinclude +++ /dev/null @@ -1,116 +0,0 @@ -<#@ template language="C#"#> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ output extension=".g.cs"#> -// ===================== -// Auto generated file -// ===================== -<#+ - /// - /// A model representing the info on an enumerable type - /// - sealed class EnumerableTypeInfo - { - public EnumerableTypeInfo( - string type, - string xmlType, - string name, - string size, - string destinationType, - string cast) - { - Type = type; - XmlType = xmlType; - Name = name; - Size = size; - DestinationType = destinationType; - Cast = cast; - } - - /// - /// Gets the name of the current type - /// - public string Type { get; } - - /// - /// Gets the XML-formatted name of the current type (eg. with {T} instead of <T>) - /// - public string XmlType { get; } - - /// - /// Gets the variable name to use - /// - public string Name { get; } - - /// - /// Gets the name of the property to use to retrieve the length of the current type - /// - public string Size { get; } - - /// - /// Gets whether or not the current type has a "Count" property - /// - public bool HasCountProperty => Size == "Count"; - - /// - /// Gets the name of the destination type, when comparing counts across different collections - /// - public string DestinationType { get; } - - /// - /// Gets the (optional) casting to resolve the diamond problem between different interfaces - /// - public string Cast { get; } - } - - /// - /// Gets the list of available enumerable types to generate APIs for - /// - static readonly IReadOnlyList EnumerableTypes = new[] - { - new EnumerableTypeInfo("Span", "", "span", "Length", "Span", ""), - new EnumerableTypeInfo("ReadOnlySpan", "", "span", "Length", "Span", ""), - new EnumerableTypeInfo("Memory", "", "memory", "Length", "Memory", ""), - new EnumerableTypeInfo("ReadOnlyMemory", "", "memory", "Length", "Memory", ""), - new EnumerableTypeInfo("T[]", " array", "array", "Length", "T[]", ""), - new EnumerableTypeInfo("List", "", "list", "Count", "List", "(ICollection)"), - new EnumerableTypeInfo("ICollection", "", "collection", "Count", "ICollection", ""), - new EnumerableTypeInfo("IReadOnlyCollection", "", "collection", "Count", "ICollection", ""), - }; - - /// - /// Gets the list of available numeric types to generate APIs for - /// - static readonly IReadOnlyList<(string Name, string Prefix)> NumericTypes = new[] - { - ("byte", "cref"), - ("sbyte", "cref"), - ("short", "cref"), - ("ushort", "cref"), - ("char", "cref"), - ("int", "cref"), - ("uint", "cref"), - ("float", "cref"), - ("long", "cref"), - ("ulong", "cref"), - ("double", "cref"), - ("decimal", "cref"), - ("nint", "langword"), - ("nuint", "langword") - }; - - /// - /// Generates text for a given sequence of items, automatically adding the necessary spacing - /// - void GenerateTextForItems(IReadOnlyList items, Action factory) - { - for (int i = 0; i < items.Count; i++) - { - // Insert a blank line after the first item - if (i > 0) WriteLine(""); - - // Invoke the factory with the current item - factory(items [i]); - } - } -#> \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Generic.cs b/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Generic.cs deleted file mode 100644 index 6b68e27ef2b..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Generic.cs +++ /dev/null @@ -1,445 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Asserts that the input value is . - /// - /// The type of value type being tested. - /// The input value to test. - /// The name of the input parameter being tested. - /// Thrown if is not . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsDefault(T value, string name) - where T : struct, IEquatable - { - if (value.Equals(default)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsDefault(value, name); - } - - /// - /// Asserts that the input value is not . - /// - /// The type of value type being tested. - /// The input value to test. - /// The name of the input parameter being tested. - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotDefault(T value, string name) - where T : struct, IEquatable - { - if (!value.Equals(default)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotDefault(name); - } - - /// - /// Asserts that the input value must be equal to a specified value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is != . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEqualTo(T value, T target, string name) - where T : notnull, IEquatable - { - if (value.Equals(target)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be not equal to a specified value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is == . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEqualTo(T value, T target, string name) - where T : notnull, IEquatable - { - if (!value.Equals(target)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEqualTo(value, target, name); - } - - /// - /// Asserts that the input value must be a bitwise match with a specified value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is not a bitwise match for . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void IsBitwiseEqualTo(T value, T target, string name) - where T : unmanaged - { - // Include some fast paths if the input type is of size 1, 2, 4, 8, or 16. - // In those cases, just reinterpret the bytes as values of an integer type, - // and compare them directly, which is much faster than having a loop over each byte. - // The conditional branches below are known at compile time by the JIT compiler, - // so that only the right one will actually be translated into native code. - if (sizeof(T) == 1) - { - byte valueByte = Unsafe.As(ref value); - byte targetByte = Unsafe.As(ref target); - - if (valueByte == targetByte) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); - } - else if (sizeof(T) == 2) - { - ushort valueUShort = Unsafe.As(ref value); - ushort targetUShort = Unsafe.As(ref target); - - if (valueUShort == targetUShort) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); - } - else if (sizeof(T) == 4) - { - uint valueUInt = Unsafe.As(ref value); - uint targetUInt = Unsafe.As(ref target); - - if (valueUInt == targetUInt) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); - } - else if (sizeof(T) == 8) - { - ulong valueULong = Unsafe.As(ref value); - ulong targetULong = Unsafe.As(ref target); - - if (Bit64Compare(ref valueULong, ref targetULong)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); - } - else if (sizeof(T) == 16) - { - ulong valueULong0 = Unsafe.As(ref value); - ulong targetULong0 = Unsafe.As(ref target); - - if (Bit64Compare(ref valueULong0, ref targetULong0)) - { - ulong valueULong1 = Unsafe.Add(ref Unsafe.As(ref value), 1); - ulong targetULong1 = Unsafe.Add(ref Unsafe.As(ref target), 1); - - if (Bit64Compare(ref valueULong1, ref targetULong1)) - { - return; - } - } - - ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); - } - else - { - Span valueBytes = new Span(Unsafe.AsPointer(ref value), sizeof(T)); - Span targetBytes = new Span(Unsafe.AsPointer(ref target), sizeof(T)); - - if (valueBytes.SequenceEqual(targetBytes)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); - } - } - - // Compares 64 bits of data from two given memory locations for bitwise equality - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe bool Bit64Compare(ref ulong left, ref ulong right) - { - // Handles 32 bit case, because using ulong is inefficient - if (sizeof(IntPtr) == 4) - { - ref int r0 = ref Unsafe.As(ref left); - ref int r1 = ref Unsafe.As(ref right); - - return r0 == r1 && - Unsafe.Add(ref r0, 1) == Unsafe.Add(ref r1, 1); - } - - return left == right; - } - - /// - /// Asserts that the input value must be less than a specified value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThan(T value, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(maximum) < 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThan(value, maximum, name); - } - - /// - /// Asserts that the input value must be less than or equal to a specified value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsLessThanOrEqualTo(T value, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(maximum) <= 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(value, maximum, name); - } - - /// - /// Asserts that the input value must be greater than a specified value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThan(T value, T minimum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) > 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThan(value, minimum, name); - } - - /// - /// Asserts that the input value must be greater than or equal to a specified value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsGreaterThanOrEqualTo(T value, T minimum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) >= 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(value, minimum, name); - } - - /// - /// Asserts that the input value must be in a given range. - /// - /// The type of input values to compare. - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or >= . - /// - /// This API asserts the equivalent of " in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRange(T value, T minimum, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) >= 0 && value.CompareTo(maximum) < 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given range. - /// - /// The type of input values to compare. - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or < . - /// - /// This API asserts the equivalent of " not in [, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRange(T value, T minimum, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) < 0 || value.CompareTo(maximum) >= 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRange(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The type of input values to compare. - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is <= or >= . - /// - /// This API asserts the equivalent of " in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetween(T value, T minimum, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) > 0 && value.CompareTo(maximum) < 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The type of input values to compare. - /// The input value to test. - /// The exclusive minimum value that is accepted. - /// The exclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is > or < . - /// - /// This API asserts the equivalent of " not in (, )", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetween(T value, T minimum, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) <= 0 || value.CompareTo(maximum) >= 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetween(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must be in a given interval. - /// - /// The type of input values to compare. - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is < or > . - /// - /// This API asserts the equivalent of " in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsBetweenOrEqualTo(T value, T minimum, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) >= 0 && value.CompareTo(maximum) <= 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(value, minimum, maximum, name); - } - - /// - /// Asserts that the input value must not be in a given interval. - /// - /// The type of input values to compare. - /// The input value to test. - /// The inclusive minimum value that is accepted. - /// The inclusive maximum value that is accepted. - /// The name of the input parameter being tested. - /// Thrown if is >= or <= . - /// - /// This API asserts the equivalent of " not in [, ]", using arithmetic notation. - /// The method is generic to avoid boxing the parameters, if they are value types. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotBetweenOrEqualTo(T value, T minimum, T maximum, string name) - where T : notnull, IComparable - { - if (value.CompareTo(minimum) < 0 || value.CompareTo(maximum) > 0) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Numeric.cs b/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Numeric.cs deleted file mode 100644 index 2403a884d2e..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Guard.Comparable.Numeric.cs +++ /dev/null @@ -1,271 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Asserts that the input value must be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCloseTo(int value, int target, uint delta, string name) - { - uint difference; - - if (value >= target) - { - difference = (uint)(value - target); - } - else - { - difference = (uint)(target - value); - } - - if (difference <= delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must not be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCloseTo(int value, int target, uint delta, string name) - { - uint difference; - - if (value >= target) - { - difference = (uint)(value - target); - } - else - { - difference = (uint)(target - value); - } - - if (difference > delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCloseTo(long value, long target, ulong delta, string name) - { - ulong difference; - - if (value >= target) - { - difference = (ulong)(value - target); - } - else - { - difference = (ulong)(target - value); - } - - if (difference <= delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must not be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCloseTo(long value, long target, ulong delta, string name) - { - ulong difference; - - if (value >= target) - { - difference = (ulong)(value - target); - } - else - { - difference = (ulong)(target - value); - } - - if (difference > delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCloseTo(float value, float target, float delta, string name) - { - if (Math.Abs(value - target) <= delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must not be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCloseTo(float value, float target, float delta, string name) - { - if (Math.Abs(value - target) > delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCloseTo(double value, double target, double delta, string name) - { - if (Math.Abs(value - target) <= delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must not be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCloseTo(double value, double target, double delta, string name) - { - if (Math.Abs(value - target) > delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCloseTo(nint value, nint target, nuint delta, string name) - { - nuint difference; - - if (value >= target) - { - difference = (nuint)(value - target); - } - else - { - difference = (nuint)(target - value); - } - - if (difference <= delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCloseTo(value, target, delta, name); - } - - /// - /// Asserts that the input value must not be within a given distance from a specified value. - /// - /// The input value to test. - /// The target value to test for. - /// The maximum distance to allow between and . - /// The name of the input parameter being tested. - /// Thrown if ( - ) <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCloseTo(nint value, nint target, nuint delta, string name) - { - nuint difference; - - if (value >= target) - { - difference = (nuint)(value - target); - } - else - { - difference = (nuint)(target - value); - } - - if (difference > delta) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Guard.IO.cs b/Microsoft.Toolkit.Diagnostics/Guard.IO.cs deleted file mode 100644 index d97d4e83a71..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Guard.IO.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Asserts that the input instance must support reading. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if doesn't support reading. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CanRead(Stream stream, string name) - { - if (stream.CanRead) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForCanRead(stream, name); - } - - /// - /// Asserts that the input instance must support writing. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if doesn't support writing. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CanWrite(Stream stream, string name) - { - if (stream.CanWrite) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForCanWrite(stream, name); - } - - /// - /// Asserts that the input instance must support seeking. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if doesn't support seeking. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CanSeek(Stream stream, string name) - { - if (stream.CanSeek) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForCanSeek(stream, name); - } - - /// - /// Asserts that the input instance must be at the starting position. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is not at the starting position. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsAtStartPosition(Stream stream, string name) - { - if (stream.Position == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsAtStartPosition(stream, name); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Guard.String.cs b/Microsoft.Toolkit.Diagnostics/Guard.String.cs deleted file mode 100644 index e80510e3a60..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Guard.String.cs +++ /dev/null @@ -1,410 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -#pragma warning disable CS8777 - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Asserts that the input instance must be or empty. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is neither nor empty. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNullOrEmpty(string? text, string name) - { - if (string.IsNullOrEmpty(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNullOrEmpty(text, name); - } - - /// - /// Asserts that the input instance must not be or empty. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is . - /// Thrown if is empty. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotNullOrEmpty([NotNull] string? text, string name) - { - if (!string.IsNullOrEmpty(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotNullOrEmpty(text, name); - } - - /// - /// Asserts that the input instance must be or whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is neither nor whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNullOrWhiteSpace(string? text, string name) - { - if (string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNullOrWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must be or whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is neither nor whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Use " + nameof(IsNullOrWhiteSpace))] - public static void IsNullOrWhitespace(string? text, string name) - { - if (string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNullOrWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must not be or whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is . - /// Thrown if is whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotNullOrWhiteSpace([NotNull] string? text, string name) - { - if (!string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotNullOrWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must not be or whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is or whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Use " + nameof(IsNotNullOrWhiteSpace))] - public static void IsNotNullOrWhitespace([NotNull] string? text, string name) - { - if (!string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotNullOrWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must be empty. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is empty. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsEmpty(string text, string name) - { - if (text.Length == 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsEmpty(text, name); - } - - /// - /// Asserts that the input instance must not be empty. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is empty. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotEmpty(string text, string name) - { - if (text.Length != 0) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotEmpty(text, name); - } - - /// - /// Asserts that the input instance must be whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is neither nor whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsWhiteSpace(string text, string name) - { - if (string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must be whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is neither nor whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Use " + nameof(IsWhiteSpace))] - public static void IsWhitespace(string text, string name) - { - if (string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must not be or whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is or whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotWhiteSpace(string text, string name) - { - if (!string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must not be or whitespace. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is or whitespace. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Use " + nameof(IsNotWhiteSpace))] - public static void IsNotWhitespace(string text, string name) - { - if (!string.IsNullOrWhiteSpace(text)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotWhiteSpace(text, name); - } - - /// - /// Asserts that the input instance must have a size of a specified value. - /// - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is != . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(string text, int size, string name) - { - if (text.Length == size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(text, size, name); - } - - /// - /// Asserts that the input instance must have a size not equal to a specified value. - /// - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is == . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeNotEqualTo(string text, int size, string name) - { - if (text.Length != size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeNotEqualTo(text, size, name); - } - - /// - /// Asserts that the input instance must have a size over a specified value. - /// - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is <= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThan(string text, int size, string name) - { - if (text.Length > size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThan(text, size, name); - } - - /// - /// Asserts that the input instance must have a size of at least specified value. - /// - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is < . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeGreaterThanOrEqualTo(string text, int size, string name) - { - if (text.Length >= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeGreaterThanOrEqualTo(text, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than a specified value. - /// - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is >= . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThan(string text, int size, string name) - { - if (text.Length < size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThan(text, size, name); - } - - /// - /// Asserts that the input instance must have a size of less than or equal to a specified value. - /// - /// The input instance to check the size for. - /// The target size to test. - /// The name of the input parameter being tested. - /// Thrown if the size of is > . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(string text, int size, string name) - { - if (text.Length <= size) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(text, size, name); - } - - /// - /// Asserts that the source instance must have the same size of a destination instance. - /// - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is != the one of . - /// The type is immutable, but the name of this API is kept for consistency with the other overloads. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeEqualTo(string source, string destination, string name) - { - if (source.Length == destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(source, destination, name); - } - - /// - /// Asserts that the source instance must have a size of less than or equal to that of a destination instance. - /// - /// The source instance to check the size for. - /// The destination instance to check the size for. - /// The name of the input parameter being tested. - /// Thrown if the size of is > the one of . - /// The type is immutable, but the name of this API is kept for consistency with the other overloads. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasSizeLessThanOrEqualTo(string source, string destination, string name) - { - if (source.Length <= destination.Length) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(source, destination, name); - } - - /// - /// Asserts that the input index is valid for a given instance. - /// - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is not valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsInRangeFor(int index, string text, string name) - { - if ((uint)index < (uint)text.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsInRangeFor(index, text, name); - } - - /// - /// Asserts that the input index is not valid for a given instance. - /// - /// The input index to be used to access . - /// The input instance to use to validate . - /// The name of the input parameter being tested. - /// Thrown if is valid to access . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotInRangeFor(int index, string text, string name) - { - if ((uint)index >= (uint)text.Length) - { - return; - } - - ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, text, name); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Guard.Tasks.cs b/Microsoft.Toolkit.Diagnostics/Guard.Tasks.cs deleted file mode 100644 index f2733865c37..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Guard.Tasks.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Asserts that the input instance is in a completed state. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is not in a completed state. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCompleted(Task task, string name) - { - if (task.IsCompleted) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCompleted(task, name); - } - - /// - /// Asserts that the input instance is not in a completed state. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is in a completed state. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCompleted(Task task, string name) - { - if (!task.IsCompleted) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCompleted(task, name); - } - - /// - /// Asserts that the input instance has been completed successfully. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if has not been completed successfully. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCompletedSuccessfully(Task task, string name) - { - if (task.Status == TaskStatus.RanToCompletion) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCompletedSuccessfully(task, name); - } - - /// - /// Asserts that the input instance has not been completed successfully. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if has been completed successfully. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCompletedSuccessfully(Task task, string name) - { - if (task.Status != TaskStatus.RanToCompletion) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCompletedSuccessfully(task, name); - } - - /// - /// Asserts that the input instance is faulted. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is not faulted. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsFaulted(Task task, string name) - { - if (task.IsFaulted) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsFaulted(task, name); - } - - /// - /// Asserts that the input instance is not faulted. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is faulted. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotFaulted(Task task, string name) - { - if (!task.IsFaulted) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotFaulted(task, name); - } - - /// - /// Asserts that the input instance is canceled. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is not canceled. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsCanceled(Task task, string name) - { - if (task.IsCanceled) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsCanceled(task, name); - } - - /// - /// Asserts that the input instance is not canceled. - /// - /// The input instance to test. - /// The name of the input parameter being tested. - /// Thrown if is canceled. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotCanceled(Task task, string name) - { - if (!task.IsCanceled) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotCanceled(task, name); - } - - /// - /// Asserts that the input instance has a specific status. - /// - /// The input instance to test. - /// The task status that is accepted. - /// The name of the input parameter being tested. - /// Thrown if doesn't match . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasStatusEqualTo(Task task, TaskStatus status, string name) - { - if (task.Status == status) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasStatusEqualTo(task, status, name); - } - - /// - /// Asserts that the input instance has not a specific status. - /// - /// The input instance to test. - /// The task status that is accepted. - /// The name of the input parameter being tested. - /// Thrown if matches . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void HasStatusNotEqualTo(Task task, TaskStatus status, string name) - { - if (task.Status != status) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForHasStatusNotEqualTo(task, status, name); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Guard.cs b/Microsoft.Toolkit.Diagnostics/Guard.cs deleted file mode 100644 index 73f173d6477..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Guard.cs +++ /dev/null @@ -1,352 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - [DebuggerStepThrough] - public static partial class Guard - { - /// - /// Asserts that the input value is . - /// - /// The type of reference value type being tested. - /// The input value to test. - /// The name of the input parameter being tested. - /// Thrown if is not . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNull(T? value, string name) - where T : class - { - if (value is null) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNull(value, name); - } - - /// - /// Asserts that the input value is . - /// - /// The type of nullable value type being tested. - /// The input value to test. - /// The name of the input parameter being tested. - /// Thrown if is not . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNull(T? value, string name) - where T : struct - { - if (value is null) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNull(value, name); - } - - /// - /// Asserts that the input value is not . - /// - /// The type of reference value type being tested. - /// The input value to test. - /// The name of the input parameter being tested. - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotNull([NotNull] T? value, string name) - where T : class - { - if (value is not null) - { - return; - } - - ThrowHelper.ThrowArgumentNullExceptionForIsNotNull(name); - } - - /// - /// Asserts that the input value is not . - /// - /// The type of nullable value type being tested. - /// The input value to test. - /// The name of the input parameter being tested. - /// Thrown if is . - /// The method is generic to avoid boxing the parameters, if they are value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotNull([NotNull] T? value, string name) - where T : struct - { - if (value is not null) - { - return; - } - - ThrowHelper.ThrowArgumentNullExceptionForIsNotNull(name); - } - - /// - /// Asserts that the input value is of a specific type. - /// - /// The type of the input value. - /// The input to test. - /// The name of the input parameter being tested. - /// Thrown if is not of type . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsOfType(object value, string name) - { - if (value.GetType() == typeof(T)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsOfType(value, name); - } - - /// - /// Asserts that the input value is not of a specific type. - /// - /// The type of the input value. - /// The input to test. - /// The name of the input parameter being tested. - /// Thrown if is of type . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotOfType(object value, string name) - { - if (value.GetType() != typeof(T)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotOfType(value, name); - } - - /// - /// Asserts that the input value is of a specific type. - /// - /// The input to test. - /// The type to look for. - /// The name of the input parameter being tested. - /// Thrown if the type of is not the same as . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsOfType(object value, Type type, string name) - { - if (value.GetType() == type) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsOfType(value, type, name); - } - - /// - /// Asserts that the input value is not of a specific type. - /// - /// The input to test. - /// The type to look for. - /// The name of the input parameter being tested. - /// Thrown if the type of is the same as . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotOfType(object value, Type type, string name) - { - if (value.GetType() != type) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotOfType(value, type, name); - } - - /// - /// Asserts that the input value can be assigned to a specified type. - /// - /// The type to check the input value against. - /// The input to test. - /// The name of the input parameter being tested. - /// Thrown if can't be assigned to type . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsAssignableToType(object value, string name) - { - if (value is T) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsAssignableToType(value, name); - } - - /// - /// Asserts that the input value can't be assigned to a specified type. - /// - /// The type to check the input value against. - /// The input to test. - /// The name of the input parameter being tested. - /// Thrown if can be assigned to type . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotAssignableToType(object value, string name) - { - if (value is not T) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotAssignableToType(value, name); - } - - /// - /// Asserts that the input value can be assigned to a specified type. - /// - /// The input to test. - /// The type to look for. - /// The name of the input parameter being tested. - /// Thrown if can't be assigned to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsAssignableToType(object value, Type type, string name) - { - if (type.IsInstanceOfType(value)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsAssignableToType(value, type, name); - } - - /// - /// Asserts that the input value can't be assigned to a specified type. - /// - /// The input to test. - /// The type to look for. - /// The name of the input parameter being tested. - /// Thrown if can be assigned to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsNotAssignableToType(object value, Type type, string name) - { - if (!type.IsInstanceOfType(value)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsNotAssignableToType(value, type, name); - } - - /// - /// Asserts that the input value must be the same instance as the target value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is not the same instance as . - /// The method is generic to prevent using it with value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsReferenceEqualTo(T value, T target, string name) - where T : class - { - if (ReferenceEquals(value, target)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsReferenceEqualTo(name); - } - - /// - /// Asserts that the input value must not be the same instance as the target value. - /// - /// The type of input values to compare. - /// The input value to test. - /// The target value to test for. - /// The name of the input parameter being tested. - /// Thrown if is the same instance as . - /// The method is generic to prevent using it with value types. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsReferenceNotEqualTo(T value, T target, string name) - where T : class - { - if (!ReferenceEquals(value, target)) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsReferenceNotEqualTo(name); - } - - /// - /// Asserts that the input value must be . - /// - /// The input to test. - /// The name of the input parameter being tested. - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsTrue([DoesNotReturnIf(false)] bool value, string name) - { - if (value) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsTrue(name); - } - - /// - /// Asserts that the input value must be . - /// - /// The input to test. - /// The name of the input parameter being tested. - /// A message to display if is . - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsTrue([DoesNotReturnIf(false)] bool value, string name, string message) - { - if (value) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsTrue(name, message); - } - - /// - /// Asserts that the input value must be . - /// - /// The input to test. - /// The name of the input parameter being tested. - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsFalse([DoesNotReturnIf(true)] bool value, string name) - { - if (!value) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsFalse(name); - } - - /// - /// Asserts that the input value must be . - /// - /// The input to test. - /// The name of the input parameter being tested. - /// A message to display if is . - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IsFalse([DoesNotReturnIf(true)] bool value, string name, string message) - { - if (!value) - { - return; - } - - ThrowHelper.ThrowArgumentExceptionForIsFalse(name, message); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs deleted file mode 100644 index 8fa5684911d..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Throws an when fails. - /// - /// The item of items in the input instance. - /// This method is needed because can't be used as a generic type parameter. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotEmptyWithSpan(string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(Span).ToTypeString()}) must not be empty", name); - } - - /// - /// Throws an when fails. - /// - /// The item of items in the input instance. - /// This method is needed because can't be used as a generic type parameter. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotEmptyWithReadOnlySpan(string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(ReadOnlySpan).ToTypeString()}) must not be empty", name); - } - - /// - /// Throws an when (or an overload) fails. - /// - /// The item of items in the input collection. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotEmpty(string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be empty", name); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs deleted file mode 100644 index 00b6478b3a2..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs +++ /dev/null @@ -1,174 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Throws an when fails. - /// - /// The type of value type being tested. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsDefault(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); - } - - /// - /// Throws an when fails. - /// - /// The type of value type being tested. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotDefault(string name) - where T : struct - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must not be the default value {AssertString(default(T))}", name); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEqualTo(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); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotEqualTo(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); - } - - /// - /// Throws an when fails. - /// - /// The type of input values being compared. - [DoesNotReturn] - public static void ThrowArgumentExceptionForBitwiseEqualTo(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); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsLessThan(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsLessThanOrEqualTo(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsGreaterThan(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsGreaterThanOrEqualTo(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsInRange(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotInRange(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsBetween(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotBetween(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsBetweenOrEqualTo(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)}"); - } - - /// - /// Throws an when fails. - /// - /// The type of values being tested. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(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)}"); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs deleted file mode 100644 index 795ff436a59..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Internals/Guard.IO.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.IO.ThrowHelper.cs deleted file mode 100644 index a781ee8742a..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Internals/Guard.IO.ThrowHelper.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.IO; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForCanRead(Stream stream, string name) - { - throw new ArgumentException($"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support reading", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForCanWrite(Stream stream, string name) - { - throw new ArgumentException($"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support writing", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForCanSeek(Stream stream, string name) - { - throw new ArgumentException($"Stream {AssertString(name)} ({stream.GetType().ToTypeString()}) doesn't support seeking", name); - } - - /// - /// Throws an when fails. - /// - [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); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Internals/Guard.String.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.String.ThrowHelper.cs deleted file mode 100644 index 4af573d34f9..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Internals/Guard.String.ThrowHelper.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an or when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotNullOrEmpty(string? text, string name) - { - [MethodImpl(MethodImplOptions.NoInlining)] - static Exception GetException(string? text, string name) - { - if (text is null) - { - return new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or empty, was null"); - } - - return new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or empty, was empty", name); - } - - throw GetException(text, name); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotNullOrWhiteSpace(string? text, string name) - { - [MethodImpl(MethodImplOptions.NoInlining)] - static Exception GetException(string? text, string name) - { - if (text is null) - { - return new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or whitespace, was null"); - } - - return new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or whitespace, was whitespace", name); - } - - throw GetException(text, name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsEmpty(string text, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} (string) must be empty, was {AssertString(text)}", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotEmpty(string text, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} (string) must not be empty", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsWhiteSpace(string text, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} (string) must be whitespace, was {AssertString(text)}", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotWhiteSpace(string text, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} (string) must not be whitespace, was {AssertString(text)}", name); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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)}"); - } - - /// - /// Throws an when fails. - /// - [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)}"); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs deleted file mode 100644 index 7093be2fc7c..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs +++ /dev/null @@ -1,112 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Internals/Guard.ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/Internals/Guard.ThrowHelper.cs deleted file mode 100644 index ebe28da2904..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Internals/Guard.ThrowHelper.cs +++ /dev/null @@ -1,204 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to verify conditions when running code. - /// - public static partial class Guard - { - /// - /// Helper methods to efficiently throw exceptions. - /// - private static partial class ThrowHelper - { - /// - /// Returns a formatted representation of the input value. - /// - /// The input to format. - /// A formatted representation of to display in error messages. - [Pure] - private static string AssertString(object? obj) - { - return obj switch - { - string _ => $"\"{obj}\"", - null => "null", - _ => $"<{obj}>" - }; - } - - /// - /// Throws an when (where is ) fails. - /// - /// The type of the input value. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNull(T value, string name) - where T : class - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be null, was {AssertString(value)} ({value.GetType().ToTypeString()})", name); - } - - /// - /// Throws an when (where is ) fails. - /// - /// The type of the input value. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNull(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); - } - - /// - /// Throws an when fails. - /// - /// The type of the input value. - [DoesNotReturn] - public static void ThrowArgumentNullExceptionForIsNotNull(string name) - { - throw new ArgumentNullException(name, $"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be not null)"); - } - - /// - /// Throws an when fails. - /// - /// The type of the input value. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsOfType(object value, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} must be of type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}", name); - } - - /// - /// Throws an when fails. - /// - /// The type of the input value. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotOfType(object value, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} must not be of type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}", name); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - /// The type being checked against. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsAssignableToType(object value, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} must be assignable to type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}", name); - } - - /// - /// Throws an when fails. - /// - /// The type being checked against. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsNotAssignableToType(object value, string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} must not be assignable to type {typeof(T).ToTypeString()}, was {value.GetType().ToTypeString()}", name); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - [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); - } - - /// - /// Throws an when fails. - /// - /// The type of input value being compared. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsReferenceEqualTo(string name) - where T : class - { - throw new ArgumentException($"Parameter {AssertString(name)} ({typeof(T).ToTypeString()}) must be the same instance as the target object", name); - } - - /// - /// Throws an when fails. - /// - /// The type of input value being compared. - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsReferenceNotEqualTo(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); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsTrue(string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} must be true, was false", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsTrue(string name, string message) - { - throw new ArgumentException($"Parameter {AssertString(name)} must be true, was false: {AssertString(message)}", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsFalse(string name) - { - throw new ArgumentException($"Parameter {AssertString(name)} must be false, was true", name); - } - - /// - /// Throws an when fails. - /// - [DoesNotReturn] - public static void ThrowArgumentExceptionForIsFalse(string name, string message) - { - throw new ArgumentException($"Parameter {AssertString(name)} must be false, was true: {AssertString(message)}", name); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/Microsoft.Toolkit.Diagnostics.csproj b/Microsoft.Toolkit.Diagnostics/Microsoft.Toolkit.Diagnostics.csproj deleted file mode 100644 index 9c1f7d0a868..00000000000 --- a/Microsoft.Toolkit.Diagnostics/Microsoft.Toolkit.Diagnostics.csproj +++ /dev/null @@ -1,100 +0,0 @@ - - - - Enable - true - netstandard1.4;netstandard2.0;netstandard2.1;net5.0 - - - - Windows Community Toolkit - Diagnostics (.NET Standard) - - This package includes .NET Standard code only helpers such as: - - Guard: Helper methods to verify conditions when running code. - - ThrowHelper: Helper methods to efficiently throw exceptions. - - Diagnostics;Guard;ThrowHelper;TypeInfo;Extensions;Helpers - - - - - - - - - - - - - - - - - - - - - - NETSTANDARD2_1_OR_GREATER - - - - - - - - - - NETSTANDARD2_1_OR_GREATER - - - - - - - TextTemplatingFileGenerator - Guard.Comparable.Numeric.g.cs - - - TextTemplatingFileGenerator - Guard.Collection.g.cs - - - TextTemplatingFileGenerator - ThrowHelper.Collection.g.cs - - - TextTemplatingFileGenerator - TypeInfo.g.cs - - - - - - - - - - - True - True - Guard.Comparable.Numeric.tt - - - True - True - Guard.Collection.tt - - - True - True - ThrowHelper.Collection.tt - - - True - True - TypeInfo.ttinclude - - - - \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/ThrowHelper.Generic.cs b/Microsoft.Toolkit.Diagnostics/ThrowHelper.Generic.cs deleted file mode 100644 index 902032b4a58..00000000000 --- a/Microsoft.Toolkit.Diagnostics/ThrowHelper.Generic.cs +++ /dev/null @@ -1,1102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.IO; -#if !NETSTANDARD1_4 -using System.Runtime.InteropServices; -#endif -using System.Threading; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to efficiently throw exceptions. - /// - public static partial class ThrowHelper - { - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArrayTypeMismatchException() - { - throw new ArrayTypeMismatchException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArrayTypeMismatchException(string? message) - { - throw new ArrayTypeMismatchException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArrayTypeMismatchException(string? message, Exception? innerException) - { - throw new ArrayTypeMismatchException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentException() - { - throw new ArgumentException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentException(string? message) - { - throw new ArgumentException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentException(string? message, Exception? innerException) - { - throw new ArgumentException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The message to include in the exception. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentException(string? name, string? message) - { - throw new ArgumentException(message, name); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentException(string? name, string? message, Exception? innerException) - { - throw new ArgumentException(message, name, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentNullException() - { - throw new ArgumentNullException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentNullException(string? name) - { - throw new ArgumentNullException(name); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentNullException(string? name, Exception? innerException) - { - throw new ArgumentNullException(name, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The message to include in the exception. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentNullException(string? name, string? message) - { - throw new ArgumentNullException(name, message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentOutOfRangeException() - { - throw new ArgumentOutOfRangeException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentOutOfRangeException(string? name) - { - throw new ArgumentOutOfRangeException(name); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentOutOfRangeException(string? name, Exception? innerException) - { - throw new ArgumentOutOfRangeException(name, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The message to include in the exception. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentOutOfRangeException(string? name, string? message) - { - throw new ArgumentOutOfRangeException(name, message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The current argument value. - /// The message to include in the exception. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowArgumentOutOfRangeException(string? name, object? value, string? message) - { - throw new ArgumentOutOfRangeException(name, value, message); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowCOMException() - { - throw new COMException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowCOMException(string? message) - { - throw new COMException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowCOMException(string? message, Exception? innerException) - { - throw new COMException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The HRESULT of the errror to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowCOMException(string? message, int error) - { - throw new COMException(message, error); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowExternalException() - { - throw new ExternalException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowExternalException(string? message) - { - throw new ExternalException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowExternalException(string? message, Exception? innerException) - { - throw new ExternalException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The argument name. - /// The HRESULT of the errror to include. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowExternalException(string? message, int error) - { - throw new ExternalException(message, error); - } -#endif - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowFormatException() - { - throw new FormatException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowFormatException(string? message) - { - throw new FormatException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowFormatException(string? message, Exception? innerException) - { - throw new FormatException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInsufficientMemoryException() - { - throw new InsufficientMemoryException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInsufficientMemoryException(string? message) - { - throw new InsufficientMemoryException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInsufficientMemoryException(string? message, Exception? innerException) - { - throw new InsufficientMemoryException(message, innerException); - } -#endif - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInvalidDataException() - { - throw new InvalidDataException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInvalidDataException(string? message) - { - throw new InvalidDataException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInvalidDataException(string? message, Exception? innerException) - { - throw new InvalidDataException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInvalidOperationException() - { - throw new InvalidOperationException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInvalidOperationException(string? message) - { - throw new InvalidOperationException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowInvalidOperationException(string? message, Exception? innerException) - { - throw new InvalidOperationException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowLockRecursionException() - { - throw new LockRecursionException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowLockRecursionException(string? message) - { - throw new LockRecursionException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowLockRecursionException(string? message, Exception? innerException) - { - throw new LockRecursionException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingFieldException() - { - throw new MissingFieldException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingFieldException(string? message) - { - throw new MissingFieldException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingFieldException(string? message, Exception? innerException) - { - throw new MissingFieldException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The type of expected result. - /// The target class being inspected. - /// The target field being retrieved. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingFieldException(string? className, string? fieldName) - { - throw new MissingFieldException(className, fieldName); - } -#endif - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMemberException() - { - throw new MissingMemberException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMemberException(string? message) - { - throw new MissingMemberException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMemberException(string? message, Exception? innerException) - { - throw new MissingMemberException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The type of expected result. - /// The target class being inspected. - /// The target member being retrieved. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMemberException(string? className, string? memberName) - { - throw new MissingMemberException(className, memberName); - } -#endif - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMethodException() - { - throw new MissingMethodException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMethodException(string? message) - { - throw new MissingMethodException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMethodException(string? message, Exception? innerException) - { - throw new MissingMethodException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The type of expected result. - /// The target class being inspected. - /// The target method being retrieved. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowMissingMethodException(string? className, string? methodName) - { - throw new MissingMethodException(className, methodName); - } -#endif - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowNotSupportedException() - { - throw new NotSupportedException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowNotSupportedException(string? message) - { - throw new NotSupportedException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowNotSupportedException(string? message, Exception? innerException) - { - throw new NotSupportedException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The name of the disposed object. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowObjectDisposedException(string? objectName) - { - throw new ObjectDisposedException(objectName); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The name of the disposed object. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowObjectDisposedException(string? objectName, Exception? innerException) - { - throw new ObjectDisposedException(objectName, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The name of the disposed object. - /// The message to include in the exception. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowObjectDisposedException(string? objectName, string? message) - { - throw new ObjectDisposedException(objectName, message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowOperationCanceledException() - { - throw new OperationCanceledException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowOperationCanceledException(string? message) - { - throw new OperationCanceledException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowOperationCanceledException(string? message, Exception? innerException) - { - throw new OperationCanceledException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The in use. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowOperationCanceledException(CancellationToken token) - { - throw new OperationCanceledException(token); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The in use. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowOperationCanceledException(string? message, CancellationToken token) - { - throw new OperationCanceledException(message, token); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// The in use. - /// Thrown with the specified parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowOperationCanceledException(string? message, Exception? innerException, CancellationToken token) - { - throw new OperationCanceledException(message, innerException, token); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowPlatformNotSupportedException() - { - throw new PlatformNotSupportedException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowPlatformNotSupportedException(string? message) - { - throw new PlatformNotSupportedException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowPlatformNotSupportedException(string? message, Exception? innerException) - { - throw new PlatformNotSupportedException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowSynchronizationLockException() - { - throw new SynchronizationLockException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowSynchronizationLockException(string? message) - { - throw new SynchronizationLockException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowSynchronizationLockException(string? message, Exception? innerException) - { - throw new SynchronizationLockException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowTimeoutException() - { - throw new TimeoutException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowTimeoutException(string? message) - { - throw new TimeoutException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowTimeoutException(string? message, Exception? innerException) - { - throw new TimeoutException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowUnauthorizedAccessException() - { - throw new UnauthorizedAccessException(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowUnauthorizedAccessException(string? message) - { - throw new UnauthorizedAccessException(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowUnauthorizedAccessException(string? message, Exception? innerException) - { - throw new UnauthorizedAccessException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// Thrown with no parameters. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowWin32Exception() - { - throw new Win32Exception(); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The Win32 error code associated with this exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowWin32Exception(int error) - { - throw new Win32Exception(error); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The Win32 error code associated with this exception. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowWin32Exception(int error, string? message) - { - throw new Win32Exception(error, message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowWin32Exception(string? message) - { - throw new Win32Exception(message); - } - - /// - /// Throws a new . - /// - /// The type of expected result. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - /// This method always throws, so it actually never returns a value. - [DoesNotReturn] - public static T ThrowWin32Exception(string? message, Exception? innerException) - { - throw new Win32Exception(message, innerException); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Diagnostics/ThrowHelper.cs b/Microsoft.Toolkit.Diagnostics/ThrowHelper.cs deleted file mode 100644 index 02b888fa5dc..00000000000 --- a/Microsoft.Toolkit.Diagnostics/ThrowHelper.cs +++ /dev/null @@ -1,940 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.IO; -#if !NETSTANDARD1_4 -using System.Runtime.InteropServices; -#endif -using System.Threading; - -namespace Microsoft.Toolkit.Diagnostics -{ - /// - /// Helper methods to efficiently throw exceptions. - /// - public static partial class ThrowHelper - { - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowArrayTypeMismatchException() - { - throw new ArrayTypeMismatchException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowArrayTypeMismatchException(string? message) - { - throw new ArrayTypeMismatchException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArrayTypeMismatchException(string? message, Exception? innerException) - { - throw new ArrayTypeMismatchException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowArgumentException() - { - throw new ArgumentException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowArgumentException(string? message) - { - throw new ArgumentException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArgumentException(string? message, Exception? innerException) - { - throw new ArgumentException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The message to include in the exception. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArgumentException(string? name, string? message) - { - throw new ArgumentException(message, name); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArgumentException(string? name, string? message, Exception? innerException) - { - throw new ArgumentException(message, name, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowArgumentNullException() - { - throw new ArgumentNullException(); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowArgumentNullException(string? name) - { - throw new ArgumentNullException(name); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArgumentNullException(string? name, Exception? innerException) - { - throw new ArgumentNullException(name, innerException); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The message to include in the exception. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArgumentNullException(string? name, string? message) - { - throw new ArgumentNullException(name, message); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeException() - { - throw new ArgumentOutOfRangeException(); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeException(string? name) - { - throw new ArgumentOutOfRangeException(name); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeException(string? name, Exception? innerException) - { - throw new ArgumentOutOfRangeException(name, innerException); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The message to include in the exception. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeException(string? name, string? message) - { - throw new ArgumentOutOfRangeException(name, message); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The current argument value. - /// The message to include in the exception. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowArgumentOutOfRangeException(string? name, object? value, string? message) - { - throw new ArgumentOutOfRangeException(name, value, message); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// Thrown with no paarameters. - [DoesNotReturn] - public static void ThrowCOMException() - { - throw new COMException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowCOMException(string? message) - { - throw new COMException(message); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowCOMException(string? message, Exception? innerException) - { - throw new COMException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The HRESULT of the errror to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowCOMException(string? message, int error) - { - throw new COMException(message, error); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowExternalException() - { - throw new ExternalException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowExternalException(string? message) - { - throw new ExternalException(message); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The inner to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowExternalException(string? message, Exception? innerException) - { - throw new ExternalException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The argument name. - /// The HRESULT of the errror to include. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowExternalException(string? message, int error) - { - throw new ExternalException(message, error); - } -#endif - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowFormatException() - { - throw new FormatException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowFormatException(string? message) - { - throw new FormatException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowFormatException(string? message, Exception? innerException) - { - throw new FormatException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowInsufficientMemoryException() - { - throw new InsufficientMemoryException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowInsufficientMemoryException(string? message) - { - throw new InsufficientMemoryException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowInsufficientMemoryException(string? message, Exception? innerException) - { - throw new InsufficientMemoryException(message, innerException); - } -#endif - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowInvalidDataException() - { - throw new InvalidDataException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowInvalidDataException(string? message) - { - throw new InvalidDataException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowInvalidDataException(string? message, Exception? innerException) - { - throw new InvalidDataException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowInvalidOperationException() - { - throw new InvalidOperationException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowInvalidOperationException(string? message) - { - throw new InvalidOperationException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowInvalidOperationException(string? message, Exception? innerException) - { - throw new InvalidOperationException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowLockRecursionException() - { - throw new LockRecursionException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowLockRecursionException(string? message) - { - throw new LockRecursionException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowLockRecursionException(string? message, Exception? innerException) - { - throw new LockRecursionException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowMissingFieldException() - { - throw new MissingFieldException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowMissingFieldException(string? message) - { - throw new MissingFieldException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowMissingFieldException(string? message, Exception? innerException) - { - throw new MissingFieldException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The target class being inspected. - /// The target field being retrieved. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowMissingFieldException(string? className, string? fieldName) - { - throw new MissingFieldException(className, fieldName); - } -#endif - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowMissingMemberException() - { - throw new MissingMemberException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowMissingMemberException(string? message) - { - throw new MissingMemberException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowMissingMemberException(string? message, Exception? innerException) - { - throw new MissingMemberException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The target class being inspected. - /// The target member being retrieved. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowMissingMemberException(string? className, string? memberName) - { - throw new MissingMemberException(className, memberName); - } -#endif - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowMissingMethodException() - { - throw new MissingMethodException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowMissingMethodException(string? message) - { - throw new MissingMethodException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowMissingMethodException(string? message, Exception? innerException) - { - throw new MissingMethodException(message, innerException); - } - -#if !NETSTANDARD1_4 - /// - /// Throws a new . - /// - /// The target class being inspected. - /// The target method being retrieved. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowMissingMethodException(string? className, string? methodName) - { - throw new MissingMethodException(className, methodName); - } -#endif - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowNotSupportedException() - { - throw new NotSupportedException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowNotSupportedException(string? message) - { - throw new NotSupportedException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowNotSupportedException(string? message, Exception? innerException) - { - throw new NotSupportedException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The name of the disposed object. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowObjectDisposedException(string? objectName) - { - throw new ObjectDisposedException(objectName); - } - - /// - /// Throws a new . - /// - /// The name of the disposed object. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowObjectDisposedException(string? objectName, Exception? innerException) - { - throw new ObjectDisposedException(objectName, innerException); - } - - /// - /// Throws a new . - /// - /// The name of the disposed object. - /// The message to include in the exception. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowObjectDisposedException(string? objectName, string? message) - { - throw new ObjectDisposedException(objectName, message); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowOperationCanceledException() - { - throw new OperationCanceledException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowOperationCanceledException(string? message) - { - throw new OperationCanceledException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowOperationCanceledException(string? message, Exception? innerException) - { - throw new OperationCanceledException(message, innerException); - } - - /// - /// Throws a new . - /// - /// The in use. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowOperationCanceledException(CancellationToken token) - { - throw new OperationCanceledException(token); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The in use. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowOperationCanceledException(string? message, CancellationToken token) - { - throw new OperationCanceledException(message, token); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// The in use. - /// Thrown with the specified parameters. - [DoesNotReturn] - public static void ThrowOperationCanceledException(string? message, Exception? innerException, CancellationToken token) - { - throw new OperationCanceledException(message, innerException, token); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowPlatformNotSupportedException() - { - throw new PlatformNotSupportedException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowPlatformNotSupportedException(string? message) - { - throw new PlatformNotSupportedException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowPlatformNotSupportedException(string? message, Exception? innerException) - { - throw new PlatformNotSupportedException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowSynchronizationLockException() - { - throw new SynchronizationLockException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowSynchronizationLockException(string? message) - { - throw new SynchronizationLockException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowSynchronizationLockException(string? message, Exception? innerException) - { - throw new SynchronizationLockException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowTimeoutException() - { - throw new TimeoutException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowTimeoutException(string? message) - { - throw new TimeoutException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowTimeoutException(string? message, Exception? innerException) - { - throw new TimeoutException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowUnauthorizedAccessException() - { - throw new UnauthorizedAccessException(); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowUnauthorizedAccessException(string? message) - { - throw new UnauthorizedAccessException(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowUnauthorizedAccessException(string? message, Exception? innerException) - { - throw new UnauthorizedAccessException(message, innerException); - } - - /// - /// Throws a new . - /// - /// Thrown with no parameters. - [DoesNotReturn] - public static void ThrowWin32Exception() - { - throw new Win32Exception(); - } - - /// - /// Throws a new . - /// - /// The Win32 error code associated with this exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowWin32Exception(int error) - { - throw new Win32Exception(error); - } - - /// - /// Throws a new . - /// - /// The Win32 error code associated with this exception. - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowWin32Exception(int error, string? message) - { - throw new Win32Exception(error, message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowWin32Exception(string? message) - { - throw new Win32Exception(message); - } - - /// - /// Throws a new . - /// - /// The message to include in the exception. - /// The inner to include. - /// Thrown with the specified parameter. - [DoesNotReturn] - public static void ThrowWin32Exception(string? message, Exception? innerException) - { - throw new Win32Exception(message, innerException); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Attributes/NotNullWhenAttribute.cs b/Microsoft.Toolkit.HighPerformance/Attributes/NotNullWhenAttribute.cs deleted file mode 100644 index dc2afc1783a..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Attributes/NotNullWhenAttribute.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NETSTANDARD2_1_OR_GREATER - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that when a method returns , the parameter - /// will not be null even if the corresponding type allows it. - /// - /// Internal copy of the .NET Standard 2.1 attribute. - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// - /// The return value condition. If the method returns this value, - /// the associated parameter will not be . - /// - public NotNullWhenAttribute(bool returnValue) - { - ReturnValue = returnValue; - } - - /// - /// Gets a value indicating whether the return value should be . - /// - public bool ReturnValue { get; } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Attributes/SkipLocalsInitAttribute.cs b/Microsoft.Toolkit.HighPerformance/Attributes/SkipLocalsInitAttribute.cs deleted file mode 100644 index a2343c58ae4..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Attributes/SkipLocalsInitAttribute.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NET5_0 - -namespace System.Runtime.CompilerServices -{ - /// - /// Used to indicate to the compiler that the .locals init flag should not be set in method headers. - /// - /// Internal copy of the .NET 5 attribute. - [AttributeUsage( - AttributeTargets.Module | - AttributeTargets.Class | - AttributeTargets.Struct | - AttributeTargets.Interface | - AttributeTargets.Constructor | - AttributeTargets.Method | - AttributeTargets.Property | - AttributeTargets.Event, - Inherited = false)] - internal sealed class SkipLocalsInitAttribute : Attribute - { - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Box{T}.cs b/Microsoft.Toolkit.HighPerformance/Box{T}.cs deleted file mode 100644 index 4a6d639b5b4..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Box{T}.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// A that represents a boxed value on the managed heap. - /// This is a "shadow" type that can be used in place of a non-generic reference to a - /// boxed value type, to make the code more expressive and reduce the chances of errors. - /// Consider this example: - /// - /// object obj = 42; - /// - /// // Manual, error prone unboxing - /// int sum = (int)obj + 1; - /// - /// In this example, it is not possible to know in advance what type is actually being boxed in a given - /// instance, making the code less robust at build time. The - /// type can be used as a drop-in replacement in this case, like so: - /// - /// Box<int> box = 42; - /// - /// // Build-time validation, automatic unboxing - /// int sum = box.Value + 1; - /// - /// This type can also be useful when dealing with large custom value types that are also boxed, as - /// it allows to retrieve a mutable reference to the boxing value. This means that a given boxed - /// value can be mutated in-place, instead of having to allocate a new updated boxed instance. - /// - /// The type of value being boxed. - [DebuggerDisplay("{ToString(),raw}")] - public sealed class Box - where T : struct - { - // Boxed value types in the CLR are represented in memory as simple objects that store the method table of - // the corresponding T value type being boxed, and then the data of the value being boxed: - // [ sync block || pMethodTable || boxed T value ] - // ^ ^ - // | \-- Unsafe.Unbox(Box) - // \-- Box reference - // For more info, see: https://mattwarren.org/2017/08/02/A-look-at-the-internals-of-boxing-in-the-CLR/. - // Note that there might be some padding before the actual data representing the boxed value, - // which might depend on both the runtime and the exact CPU architecture. - // This is automatically handled by the unbox !!T instruction in IL, which - // unboxes a given value type T and returns a reference to its boxed data. - - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor is never used, it is only declared in order to mark it with - /// the visibility modifier and prevent direct use. - /// - /// Always thrown when this constructor is used (eg. from reflection). - private Box() - { - throw new InvalidOperationException("The Microsoft.Toolkit.HighPerformance.Box constructor should never be used"); - } - - /// - /// Returns a reference from the input instance. - /// - /// The input instance, representing a boxed value. - /// A reference pointing to . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Box GetFrom(object obj) - { - if (obj.GetType() != typeof(T)) - { - ThrowInvalidCastExceptionForGetFrom(); - } - - return Unsafe.As>(obj)!; - } - - /// - /// Returns a reference from the input instance. - /// - /// The input instance, representing a boxed value. - /// A reference pointing to . - /// - /// This method doesn't check the actual type of , so it is responsibility of the caller - /// to ensure it actually represents a boxed value and not some other instance. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Box DangerousGetFrom(object obj) - { - return Unsafe.As>(obj)!; - } - - /// - /// Tries to get a reference from an input representing a boxed value. - /// - /// The input instance to check. - /// The resulting reference, if was a boxed value. - /// if a instance was retrieved correctly, otherwise. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetFrom(object obj, [NotNullWhen(true)] out Box? box) - { - if (obj.GetType() == typeof(T)) - { - box = Unsafe.As>(obj)!; - - return true; - } - - box = null; - - return false; - } - - /// - /// Implicitly gets the value from a given instance. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator T(Box box) - { - return (T)(object)box; - } - - /// - /// Implicitly creates a new instance from a given value. - /// - /// The input value to wrap. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Box(T value) - { - // The Box type is never actually instantiated. - // Here we are just boxing the input T value, and then reinterpreting - // that object reference as a Box reference. As such, the Box - // type is really only used as an interface to access the contents - // of a boxed value type. This also makes it so that additional methods - // like ToString() or GetHashCode() will automatically be referenced from - // the method table of the boxed object, meaning that they don't need to - // manually be implemented in the Box type. For instance, boxing a float - // and calling ToString() on it directly, on its boxed object or on a Box - // reference retrieved from it will produce the same result in all cases. - return Unsafe.As>(value)!; - } - - /// - public override string ToString() - { - // Here we're overriding the base object virtual methods to ensure - // calls to those methods have a correct results on all runtimes. - // For instance, not doing so is causing issue on .NET Core 2.1 Release - // due to how the runtime handles the Box reference to an actual - // boxed T value (not a concrete Box instance as it would expect). - // To fix that, the overrides will simply call the expected methods - // directly on the boxed T values. These methods will be directly - // invoked by the JIT compiler when using a Box reference. When - // an object reference is used instead, the call would be forwarded - // to those same methods anyway, since the method table for an object - // representing a T instance is the one of type T anyway. - return this.GetReference().ToString()!; - } - - /// - public override bool Equals(object? obj) - { - return Equals(this, obj); - } - - /// - public override int GetHashCode() - { - return this.GetReference().GetHashCode(); - } - - /// - /// Throws an when a cast from an invalid is attempted. - /// - private static void ThrowInvalidCastExceptionForGetFrom() - { - throw new InvalidCastException($"Can't cast the input object to the type Box<{typeof(T)}>"); - } - } - -#pragma warning disable SA1402 // Extensions being declared after the type they apply to -#pragma warning disable SA1204 // Extension class to replace instance methods for Box - - /// - /// Helpers for working with the type. - /// - public static class BoxExtensions - { - /// - /// Gets a reference from a instance. - /// - /// The type of reference to retrieve. - /// The input instance. - /// A reference to the boxed value within . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T GetReference(this Box box) - where T : struct - { - // The reason why this method is an extension and is not part of - // the Box type itself is that Box is really just a mask - // used over object references, but it is never actually instantiated. - // Because of this, the method table of the objects in the heap will - // be the one of type T created by the runtime, and not the one of - // the Box type. To avoid potential issues when invoking this method - // on different runtimes, which might handle that scenario differently, - // we use an extension method, which is just syntactic sugar for a static - // method belonging to another class. This isn't technically necessary, - // but it's just an extra precaution since the syntax for users remains - // exactly the same anyway. Here we just call the Unsafe.Unbox(object) - // API, which is hidden away for users of the type for simplicity. - // Note that this API will always actually involve a conditional - // branch, which is introduced by the JIT compiler to validate the - // object instance being unboxed. But since the alternative of - // manually tracking the offset to the boxed data would be both - // more error prone, and it would still introduce some overhead, - // this doesn't really matter in this case anyway. - return ref Unsafe.Unbox(box); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/ArrayPoolBufferWriter{T}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/ArrayPoolBufferWriter{T}.cs deleted file mode 100644 index 78d76036f90..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/ArrayPoolBufferWriter{T}.cs +++ /dev/null @@ -1,365 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Views; -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; - -namespace Microsoft.Toolkit.HighPerformance.Buffers -{ - /// - /// Represents a heap-based, array-backed output sink into which data can be written. - /// - /// The type of items to write to the current instance. - /// - /// This is a custom implementation that replicates the - /// functionality and API surface of the array-based buffer writer available in - /// .NET Standard 2.1, with the main difference being the fact that in this case - /// the arrays in use are rented from the shared instance, - /// and that is also available on .NET Standard 2.0. - /// - [DebuggerTypeProxy(typeof(MemoryDebugView<>))] - [DebuggerDisplay("{ToString(),raw}")] - public sealed class ArrayPoolBufferWriter : IBuffer, IMemoryOwner - { - /// - /// The default buffer size to use to expand empty arrays. - /// - private const int DefaultInitialBufferSize = 256; - - /// - /// The instance used to rent . - /// - private readonly ArrayPool pool; - - /// - /// The underlying array. - /// - private T[]? array; - -#pragma warning disable IDE0032 // Use field over auto-property (clearer and faster) - /// - /// The starting offset within . - /// - private int index; -#pragma warning restore IDE0032 - - /// - /// Initializes a new instance of the class. - /// - public ArrayPoolBufferWriter() - : this(ArrayPool.Shared, DefaultInitialBufferSize) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The instance to use. - public ArrayPoolBufferWriter(ArrayPool pool) - : this(pool, DefaultInitialBufferSize) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The minimum capacity with which to initialize the underlying buffer. - /// Thrown when is not valid. - public ArrayPoolBufferWriter(int initialCapacity) - : this(ArrayPool.Shared, initialCapacity) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The instance to use. - /// The minimum capacity with which to initialize the underlying buffer. - /// Thrown when is not valid. - public ArrayPoolBufferWriter(ArrayPool pool, int initialCapacity) - { - // Since we're using pooled arrays, we can rent the buffer with the - // default size immediately, we don't need to use lazy initialization - // to save unnecessary memory allocations in this case. - // Additionally, we don't need to manually throw the exception if - // the requested size is not valid, as that'll be thrown automatically - // by the array pool in use when we try to rent an array with that size. - this.pool = pool; - this.array = pool.Rent(initialCapacity); - this.index = 0; - } - - /// - /// Finalizes an instance of the class. - /// - ~ArrayPoolBufferWriter() => Dispose(); - - /// - Memory IMemoryOwner.Memory - { - // This property is explicitly implemented so that it's hidden - // under normal usage, as the name could be confusing when - // displayed besides WrittenMemory and GetMemory(). - // The IMemoryOwner interface is implemented primarily - // so that the AsStream() extension can be used on this type, - // allowing users to first create a ArrayPoolBufferWriter - // instance to write data to, then get a stream through the - // extension and let it take care of returning the underlying - // buffer to the shared pool when it's no longer necessary. - // Inlining is not needed here since this will always be a callvirt. - get => MemoryMarshal.AsMemory(WrittenMemory); - } - - /// - public ReadOnlyMemory WrittenMemory - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return array!.AsMemory(0, this.index); - } - } - - /// - public ReadOnlySpan WrittenSpan - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return array!.AsSpan(0, this.index); - } - } - - /// - public int WrittenCount - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.index; - } - - /// - public int Capacity - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return array!.Length; - } - } - - /// - public int FreeCapacity - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return array!.Length - this.index; - } - } - - /// - public void Clear() - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - array.AsSpan(0, this.index).Clear(); - - this.index = 0; - } - - /// - public void Advance(int count) - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - if (count < 0) - { - ThrowArgumentOutOfRangeExceptionForNegativeCount(); - } - - if (this.index > array!.Length - count) - { - ThrowArgumentExceptionForAdvancedTooFar(); - } - - this.index += count; - } - - /// - public Memory GetMemory(int sizeHint = 0) - { - CheckBufferAndEnsureCapacity(sizeHint); - - return this.array.AsMemory(this.index); - } - - /// - public Span GetSpan(int sizeHint = 0) - { - CheckBufferAndEnsureCapacity(sizeHint); - - return this.array.AsSpan(this.index); - } - - /// - /// Ensures that has enough free space to contain a given number of new items. - /// - /// The minimum number of items to ensure space for in . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CheckBufferAndEnsureCapacity(int sizeHint) - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - if (sizeHint < 0) - { - ThrowArgumentOutOfRangeExceptionForNegativeSizeHint(); - } - - if (sizeHint == 0) - { - sizeHint = 1; - } - - if (sizeHint > array!.Length - this.index) - { - ResizeBuffer(sizeHint); - } - } - - /// - /// Resizes to ensure it can fit the specified number of new items. - /// - /// The minimum number of items to ensure space for in . - [MethodImpl(MethodImplOptions.NoInlining)] - private void ResizeBuffer(int sizeHint) - { - int minimumSize = this.index + sizeHint; - - // The ArrayPool class has a maximum threshold of 1024 * 1024 for the maximum length of - // pooled arrays, and once this is exceeded it will just allocate a new array every time - // of exactly the requested size. In that case, we manually round up the requested size to - // the nearest power of two, to ensure that repeated consecutive writes when the array in - // use is bigger than that threshold don't end up causing a resize every single time. - if (minimumSize > 1024 * 1024) - { - minimumSize = BitOperations.RoundUpPowerOfTwo(minimumSize); - } - - this.pool.Resize(ref this.array, minimumSize); - } - - /// - public void Dispose() - { - T[]? array = this.array; - - if (array is null) - { - return; - } - - GC.SuppressFinalize(this); - - this.array = null; - - this.pool.Return(array); - } - - /// - [Pure] - public override string ToString() - { - // See comments in MemoryOwner about this - if (typeof(T) == typeof(char) && - this.array is char[] chars) - { - return new string(chars, 0, this.index); - } - - // Same representation used in Span - return $"Microsoft.Toolkit.HighPerformance.Buffers.ArrayPoolBufferWriter<{typeof(T)}>[{this.index}]"; - } - - /// - /// Throws an when the requested count is negative. - /// - private static void ThrowArgumentOutOfRangeExceptionForNegativeCount() - { - throw new ArgumentOutOfRangeException("count", "The count can't be a negative value"); - } - - /// - /// Throws an when the size hint is negative. - /// - private static void ThrowArgumentOutOfRangeExceptionForNegativeSizeHint() - { - throw new ArgumentOutOfRangeException("sizeHint", "The size hint can't be a negative value"); - } - - /// - /// Throws an when the requested count is negative. - /// - private static void ThrowArgumentExceptionForAdvancedTooFar() - { - throw new ArgumentException("The buffer writer has advanced too far"); - } - - /// - /// Throws an when is . - /// - private static void ThrowObjectDisposedException() - { - throw new ObjectDisposedException("The current buffer has already been disposed"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Enums/AllocationMode.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Enums/AllocationMode.cs deleted file mode 100644 index 70e1f208300..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Enums/AllocationMode.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.HighPerformance.Buffers -{ - /// - /// An that indicates a mode to use when allocating buffers. - /// - public enum AllocationMode - { - /// - /// The default allocation mode for pooled memory (rented buffers are not cleared). - /// - Default, - - /// - /// Clear pooled buffers when renting them. - /// - Clear - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Interfaces/IBuffer{T}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Interfaces/IBuffer{T}.cs deleted file mode 100644 index 25e06861c81..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Interfaces/IBuffer{T}.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; - -namespace Microsoft.Toolkit.HighPerformance.Buffers -{ - /// - /// An interface that expands with the ability to also inspect - /// the written data, and to reset the underlying buffer to write again from the start. - /// - /// The type of items in the current buffer. - public interface IBuffer : IBufferWriter - { - /// - /// Gets the data written to the underlying buffer so far, as a . - /// - ReadOnlyMemory WrittenMemory { get; } - - /// - /// Gets the data written to the underlying buffer so far, as a . - /// - ReadOnlySpan WrittenSpan { get; } - - /// - /// Gets the amount of data written to the underlying buffer so far. - /// - int WrittenCount { get; } - - /// - /// Gets the total amount of space within the underlying buffer. - /// - int Capacity { get; } - - /// - /// Gets the amount of space available that can still be written into without forcing the underlying buffer to grow. - /// - int FreeCapacity { get; } - - /// - /// Clears the data written to the underlying buffer. - /// - /// - /// You must clear the instance before trying to re-use it. - /// - void Clear(); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs deleted file mode 100644 index 22720708505..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/ArrayMemoryManager{TFrom,TTo}.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces; -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals -{ - /// - /// A custom that casts data from a array, to values. - /// - /// The source type of items to read. - /// The target type to cast the source items to. - internal sealed class ArrayMemoryManager : MemoryManager, IMemoryManager - where TFrom : unmanaged - where TTo : unmanaged - { - /// - /// The source array to read data from. - /// - private readonly TFrom[] array; - - /// - /// The starting offset within . - /// - private readonly int offset; - - /// - /// The original used length for . - /// - private readonly int length; - - /// - /// Initializes a new instance of the class. - /// - /// The source array to read data from. - /// The starting offset within . - /// The original used length for . - public ArrayMemoryManager(TFrom[] array, int offset, int length) - { - this.array = array; - this.offset = offset; - this.length = length; - } - - /// - public override Span GetSpan() - { -#if SPAN_RUNTIME_SUPPORT - ref TFrom r0 = ref this.array.DangerousGetReferenceAt(this.offset); - ref TTo r1 = ref Unsafe.As(ref r0); - int length = RuntimeHelpers.ConvertLength(this.length); - - return MemoryMarshal.CreateSpan(ref r1, length); -#else - Span span = this.array.AsSpan(this.offset, this.length); - - // We rely on MemoryMarshal.Cast here to deal with calculating the effective - // size of the new span to return. This will also make the behavior consistent - // for users that are both using this type as well as casting spans directly. - return MemoryMarshal.Cast(span); -#endif - } - - /// - public override unsafe MemoryHandle Pin(int elementIndex = 0) - { - if ((uint)elementIndex >= (uint)(this.length * Unsafe.SizeOf() / Unsafe.SizeOf())) - { - ThrowArgumentOutOfRangeExceptionForInvalidIndex(); - } - - int - bytePrefix = this.offset * Unsafe.SizeOf(), - byteSuffix = elementIndex * Unsafe.SizeOf(), - byteOffset = bytePrefix + byteSuffix; - - GCHandle handle = GCHandle.Alloc(this.array, GCHandleType.Pinned); - - ref TFrom r0 = ref this.array.DangerousGetReference(); - ref byte r1 = ref Unsafe.As(ref r0); - ref byte r2 = ref Unsafe.Add(ref r1, byteOffset); - void* pi = Unsafe.AsPointer(ref r2); - - return new MemoryHandle(pi, handle); - } - - /// - public override void Unpin() - { - } - - /// - protected override void Dispose(bool disposing) - { - } - - /// - public Memory GetMemory(int offset, int length) - where T : unmanaged - { - // We need to calculate the right offset and length of the new Memory. The local offset - // is the original offset into the wrapped TFrom[] array, while the input offset is the one - // with respect to TTo items in the Memory instance that is currently being cast. - int - absoluteOffset = this.offset + RuntimeHelpers.ConvertLength(offset), - absoluteLength = RuntimeHelpers.ConvertLength(length); - - // We have a special handling in cases where the user is circling back to the original type - // of the wrapped array. In this case we can just return a memory wrapping that array directly, - // with offset and length being adjusted, without the memory manager indirection. - if (typeof(T) == typeof(TFrom)) - { - return (Memory)(object)this.array.AsMemory(absoluteOffset, absoluteLength); - } - - return new ArrayMemoryManager(this.array, absoluteOffset, absoluteLength).Memory; - } - - /// - /// Throws an when the target index for is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForInvalidIndex() - { - throw new ArgumentOutOfRangeException("elementIndex", "The input index is not in the valid range"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/Interfaces/IMemoryManager.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Internals/Interfaces/IMemoryManager.cs deleted file mode 100644 index cbffe3be17e..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/Interfaces/IMemoryManager.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; - -namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces -{ - /// - /// An interface for a instance that can reinterpret its underlying data. - /// - internal interface IMemoryManager - { - /// - /// Creates a new that reinterprets the underlying data for the current instance. - /// - /// The target type to cast the items to. - /// The starting offset within the data store. - /// The original used length for the data store. - /// A new instance of the specified type, reinterpreting the current items. - Memory GetMemory(int offset, int length) - where T : unmanaged; - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/ProxyMemoryManager{TFrom,TTo}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Internals/ProxyMemoryManager{TFrom,TTo}.cs deleted file mode 100644 index 573593b1722..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/ProxyMemoryManager{TFrom,TTo}.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces; -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals -{ - /// - /// A custom that casts data from a of , to values. - /// - /// The source type of items to read. - /// The target type to cast the source items to. - internal sealed class ProxyMemoryManager : MemoryManager, IMemoryManager - where TFrom : unmanaged - where TTo : unmanaged - { - /// - /// The source to read data from. - /// - private readonly MemoryManager memoryManager; - - /// - /// The starting offset within . - /// - private readonly int offset; - - /// - /// The original used length for . - /// - private readonly int length; - - /// - /// Initializes a new instance of the class. - /// - /// The source to read data from. - /// The starting offset within . - /// The original used length for . - public ProxyMemoryManager(MemoryManager memoryManager, int offset, int length) - { - this.memoryManager = memoryManager; - this.offset = offset; - this.length = length; - } - - /// - public override Span GetSpan() - { - Span span = this.memoryManager.GetSpan().Slice(this.offset, this.length); - - return MemoryMarshal.Cast(span); - } - - /// - public override MemoryHandle Pin(int elementIndex = 0) - { - if ((uint)elementIndex >= (uint)(this.length * Unsafe.SizeOf() / Unsafe.SizeOf())) - { - ThrowArgumentExceptionForInvalidIndex(); - } - - int - bytePrefix = this.offset * Unsafe.SizeOf(), - byteSuffix = elementIndex * Unsafe.SizeOf(), - byteOffset = bytePrefix + byteSuffix; - -#if NETSTANDARD1_4 - int - shiftedOffset = byteOffset / Unsafe.SizeOf(), - remainder = byteOffset - (shiftedOffset * Unsafe.SizeOf()); -#else - int shiftedOffset = Math.DivRem(byteOffset, Unsafe.SizeOf(), out int remainder); -#endif - - if (remainder != 0) - { - ThrowArgumentExceptionForInvalidAlignment(); - } - - return this.memoryManager.Pin(shiftedOffset); - } - - /// - public override void Unpin() - { - this.memoryManager.Unpin(); - } - - /// - protected override void Dispose(bool disposing) - { - ((IDisposable)this.memoryManager).Dispose(); - } - - /// - public Memory GetMemory(int offset, int length) - where T : unmanaged - { - // Like in the other memory manager, calculate the absolute offset and length - int - absoluteOffset = this.offset + RuntimeHelpers.ConvertLength(offset), - absoluteLength = RuntimeHelpers.ConvertLength(length); - - // Skip one indirection level and slice the original memory manager, if possible - if (typeof(T) == typeof(TFrom)) - { - return (Memory)(object)this.memoryManager.Memory.Slice(absoluteOffset, absoluteLength); - } - - return new ProxyMemoryManager(this.memoryManager, absoluteOffset, absoluteLength).Memory; - } - - /// - /// Throws an when the target index for is invalid. - /// - private static void ThrowArgumentExceptionForInvalidIndex() - { - throw new ArgumentOutOfRangeException("elementIndex", "The input index is not in the valid range"); - } - - /// - /// Throws an when receives an invalid target index. - /// - private static void ThrowArgumentExceptionForInvalidAlignment() - { - throw new ArgumentOutOfRangeException("elementIndex", "The input index doesn't result in an aligned item access"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/RawObjectMemoryManager{T}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Internals/RawObjectMemoryManager{T}.cs deleted file mode 100644 index 6fd78e45609..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/RawObjectMemoryManager{T}.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if SPAN_RUNTIME_SUPPORT - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Helpers; - -namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals -{ - /// - /// A custom that can wrap arbitrary instances. - /// - /// The type of elements in the target memory area. - internal sealed class RawObjectMemoryManager : MemoryManager - { - /// - /// The target instance. - /// - private readonly object instance; - - /// - /// The initial offset within . - /// - private readonly IntPtr offset; - - /// - /// The length of the target memory area. - /// - private readonly int length; - - /// - /// Initializes a new instance of the class. - /// - /// The target instance. - /// The starting offset within . - /// The usable length within . - public RawObjectMemoryManager(object instance, IntPtr offset, int length) - { - this.instance = instance; - this.offset = offset; - this.length = length; - } - - /// - public override Span GetSpan() - { - ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset); - - return MemoryMarshal.CreateSpan(ref r0, this.length); - } - - /// - public override unsafe MemoryHandle Pin(int elementIndex = 0) - { - if ((uint)elementIndex >= (uint)this.length) - { - ThrowArgumentOutOfRangeExceptionForInvalidElementIndex(); - } - - // Allocating a pinned handle for the array with fail and throw an exception - // if the array contains non blittable data. This is the expected behavior and - // the same happens when trying to pin a Memory instance obtained through - // traditional means (eg. via the implicit T[] array conversion), if T is a - // reference type or a type containing some references. - GCHandle handle = GCHandle.Alloc(this.instance, GCHandleType.Pinned); - ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset); - ref T r1 = ref Unsafe.Add(ref r0, (nint)(uint)elementIndex); - void* p = Unsafe.AsPointer(ref r1); - - return new MemoryHandle(p, handle); - } - - /// - public override void Unpin() - { - } - - /// - protected override void Dispose(bool disposing) - { - } - - /// - /// Throws an when the input index for is not valid. - /// - private static void ThrowArgumentOutOfRangeExceptionForInvalidElementIndex() - { - throw new ArgumentOutOfRangeException("elementIndex", "The input element index was not in the valid range"); - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs deleted file mode 100644 index 0c2e62817d6..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Internals/StringMemoryManager{TTo}.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces; -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -namespace Microsoft.Toolkit.HighPerformance.Buffers.Internals -{ - /// - /// A custom that casts data from a to values. - /// - /// The target type to cast the source characters to. - internal sealed class StringMemoryManager : MemoryManager, IMemoryManager - where TTo : unmanaged - { - /// - /// The source to read data from. - /// - private readonly string text; - - /// - /// The starting offset within . - /// - private readonly int offset; - - /// - /// The original used length for . - /// - private readonly int length; - - /// - /// Initializes a new instance of the class. - /// - /// The source to read data from. - /// The starting offset within . - /// The original used length for . - public StringMemoryManager(string text, int offset, int length) - { - this.text = text; - this.offset = offset; - this.length = length; - } - - /// - public override Span GetSpan() - { -#if SPAN_RUNTIME_SUPPORT - ref char r0 = ref this.text.DangerousGetReferenceAt(this.offset); - ref TTo r1 = ref Unsafe.As(ref r0); - int length = RuntimeHelpers.ConvertLength(this.length); - - return MemoryMarshal.CreateSpan(ref r1, length); -#else - ReadOnlyMemory memory = this.text.AsMemory(this.offset, this.length); - Span span = MemoryMarshal.AsMemory(memory).Span; - - return MemoryMarshal.Cast(span); -#endif - } - - /// - public override unsafe MemoryHandle Pin(int elementIndex = 0) - { - if ((uint)elementIndex >= (uint)(this.length * Unsafe.SizeOf() / Unsafe.SizeOf())) - { - ThrowArgumentOutOfRangeExceptionForInvalidIndex(); - } - - int - bytePrefix = this.offset * Unsafe.SizeOf(), - byteSuffix = elementIndex * Unsafe.SizeOf(), - byteOffset = bytePrefix + byteSuffix; - - GCHandle handle = GCHandle.Alloc(this.text, GCHandleType.Pinned); - - ref char r0 = ref this.text.DangerousGetReference(); - ref byte r1 = ref Unsafe.As(ref r0); - ref byte r2 = ref Unsafe.Add(ref r1, byteOffset); - void* pi = Unsafe.AsPointer(ref r2); - - return new MemoryHandle(pi, handle); - } - - /// - public override void Unpin() - { - } - - /// - protected override void Dispose(bool disposing) - { - } - - /// - public Memory GetMemory(int offset, int length) - where T : unmanaged - { - int - absoluteOffset = this.offset + RuntimeHelpers.ConvertLength(offset), - absoluteLength = RuntimeHelpers.ConvertLength(length); - - if (typeof(T) == typeof(char)) - { - ReadOnlyMemory memory = this.text.AsMemory(absoluteOffset, absoluteLength); - - return (Memory)(object)MemoryMarshal.AsMemory(memory); - } - - return new StringMemoryManager(this.text, absoluteOffset, absoluteLength).Memory; - } - - /// - /// Throws an when the target index for is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForInvalidIndex() - { - throw new ArgumentOutOfRangeException("elementIndex", "The input index is not in the valid range"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/MemoryBufferWriter{T}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/MemoryBufferWriter{T}.cs deleted file mode 100644 index 85073104aad..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/MemoryBufferWriter{T}.cs +++ /dev/null @@ -1,192 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Views; - -namespace Microsoft.Toolkit.HighPerformance.Buffers -{ - /// - /// Represents an output sink into which data can be written, backed by a instance. - /// - /// The type of items to write to the current instance. - /// - /// This is a custom implementation that wraps a instance. - /// It can be used to bridge APIs consuming an with existing - /// instances (or objects that can be converted to a ), to ensure the data is written directly - /// to the intended buffer, with no possibility of doing additional allocations or expanding the available capacity. - /// - [DebuggerTypeProxy(typeof(MemoryDebugView<>))] - [DebuggerDisplay("{ToString(),raw}")] - public sealed class MemoryBufferWriter : IBuffer - { - /// - /// The underlying instance. - /// - private readonly Memory memory; - -#pragma warning disable IDE0032 // Use field over auto-property (like in ArrayPoolBufferWriter) - /// - /// The starting offset within . - /// - private int index; -#pragma warning restore IDE0032 - - /// - /// Initializes a new instance of the class. - /// - /// The target instance to write to. - public MemoryBufferWriter(Memory memory) - { - this.memory = memory; - } - - /// - public ReadOnlyMemory WrittenMemory - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.memory.Slice(0, this.index); - } - - /// - public ReadOnlySpan WrittenSpan - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.memory.Slice(0, this.index).Span; - } - - /// - public int WrittenCount - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.index; - } - - /// - public int Capacity - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.memory.Length; - } - - /// - public int FreeCapacity - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.memory.Length - this.index; - } - - /// - public void Clear() - { - this.memory.Slice(0, this.index).Span.Clear(); - this.index = 0; - } - - /// - public void Advance(int count) - { - if (count < 0) - { - ThrowArgumentOutOfRangeExceptionForNegativeCount(); - } - - if (this.index > this.memory.Length - count) - { - ThrowArgumentExceptionForAdvancedTooFar(); - } - - this.index += count; - } - - /// - public Memory GetMemory(int sizeHint = 0) - { - ValidateSizeHint(sizeHint); - - return this.memory.Slice(this.index); - } - - /// - public Span GetSpan(int sizeHint = 0) - { - ValidateSizeHint(sizeHint); - - return this.memory.Slice(this.index).Span; - } - - /// - /// Validates the requested size for either or . - /// - /// The minimum number of items to ensure space for in . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ValidateSizeHint(int sizeHint) - { - if (sizeHint < 0) - { - ThrowArgumentOutOfRangeExceptionForNegativeSizeHint(); - } - - if (sizeHint == 0) - { - sizeHint = 1; - } - - if (sizeHint > FreeCapacity) - { - ThrowArgumentExceptionForCapacityExceeded(); - } - } - - /// - [Pure] - public override string ToString() - { - // See comments in MemoryOwner about this - if (typeof(T) == typeof(char)) - { - return this.memory.Slice(0, this.index).ToString(); - } - - // Same representation used in Span - return $"Microsoft.Toolkit.HighPerformance.Buffers.MemoryBufferWriter<{typeof(T)}>[{this.index}]"; - } - - /// - /// Throws an when the requested count is negative. - /// - private static void ThrowArgumentOutOfRangeExceptionForNegativeCount() - { - throw new ArgumentOutOfRangeException("count", "The count can't be a negative value"); - } - - /// - /// Throws an when the size hint is negative. - /// - private static void ThrowArgumentOutOfRangeExceptionForNegativeSizeHint() - { - throw new ArgumentOutOfRangeException("sizeHint", "The size hint can't be a negative value"); - } - - /// - /// Throws an when the requested count is negative. - /// - private static void ThrowArgumentExceptionForAdvancedTooFar() - { - throw new ArgumentException("The buffer writer has advanced too far"); - } - - /// - /// Throws an when the requested size exceeds the capacity. - /// - private static void ThrowArgumentExceptionForCapacityExceeded() - { - throw new ArgumentException("The buffer writer doesn't have enough capacity left"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/MemoryOwner{T}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/MemoryOwner{T}.cs deleted file mode 100644 index 75c965a7c37..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/MemoryOwner{T}.cs +++ /dev/null @@ -1,355 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if NETCORE_RUNTIME || NET5_0 -using System.Runtime.InteropServices; -#endif -using Microsoft.Toolkit.HighPerformance.Buffers.Views; - -namespace Microsoft.Toolkit.HighPerformance.Buffers -{ - /// - /// An implementation with an embedded length and a fast accessor. - /// - /// The type of items to store in the current instance. - [DebuggerTypeProxy(typeof(MemoryDebugView<>))] - [DebuggerDisplay("{ToString(),raw}")] - public sealed class MemoryOwner : IMemoryOwner - { - /// - /// The starting offset within . - /// - private readonly int start; - -#pragma warning disable IDE0032 - /// - /// The usable length within (starting from ). - /// - private readonly int length; -#pragma warning restore IDE0032 - - /// - /// The instance used to rent . - /// - private readonly ArrayPool pool; - - /// - /// The underlying array. - /// - private T[]? array; - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// The instance to use. - /// Indicates the allocation mode to use for the new buffer to rent. - private MemoryOwner(int length, ArrayPool pool, AllocationMode mode) - { - this.start = 0; - this.length = length; - this.pool = pool; - this.array = pool.Rent(length); - - if (mode == AllocationMode.Clear) - { - this.array.AsSpan(0, length).Clear(); - } - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The starting offset within . - /// The length of the array to use. - /// The instance currently in use. - /// The input array to use. - private MemoryOwner(int start, int length, ArrayPool pool, T[] array) - { - this.start = start; - this.length = length; - this.pool = pool; - this.array = array; - } - - /// - /// Finalizes an instance of the class. - /// - ~MemoryOwner() => Dispose(); - - /// - /// Gets an empty instance. - /// - [Pure] - public static MemoryOwner Empty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(0, ArrayPool.Shared, AllocationMode.Default); - } - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Allocate(int size) => new(size, ArrayPool.Shared, AllocationMode.Default); - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// The instance currently in use. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Allocate(int size, ArrayPool pool) => new(size, pool, AllocationMode.Default); - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// Indicates the allocation mode to use for the new buffer to rent. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Allocate(int size, AllocationMode mode) => new(size, ArrayPool.Shared, mode); - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// The instance currently in use. - /// Indicates the allocation mode to use for the new buffer to rent. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Allocate(int size, ArrayPool pool, AllocationMode mode) => new(size, pool, mode); - - /// - /// Gets the number of items in the current instance - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.length; - } - - /// - public Memory Memory - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return new Memory(array!, this.start, this.length); - } - } - - /// - /// Gets a wrapping the memory belonging to the current instance. - /// - public Span Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - -#if NETCORE_RUNTIME || NET5_0 - ref T r0 = ref array!.DangerousGetReferenceAt(this.start); - - // On .NET Core runtimes, we can manually create a span from the starting reference to - // skip the argument validations, which include an explicit null check, covariance check - // for the array and the actual validation for the starting offset and target length. We - // only do this on .NET Core as we can leverage the runtime-specific array layout to get - // a fast access to the initial element, which makes this trick worth it. Otherwise, on - // runtimes where we would need to at least access a static field to retrieve the base - // byte offset within an SZ array object, we can get better performance by just using the - // default Span constructor and paying the cost of the extra conditional branches, - // especially if T is a value type, in which case the covariance check is JIT removed. - return MemoryMarshal.CreateSpan(ref r0, this.length); -#else - return new Span(array!, this.start, this.length); -#endif - } - } - - /// - /// Returns a reference to the first element within the current instance, with no bounds check. - /// - /// A reference to the first element within the current instance. - /// Thrown when the buffer in use has already been disposed. - /// - /// This method does not perform bounds checks on the underlying buffer, but does check whether - /// the buffer itself has been disposed or not. This check should not be removed, and it's also - /// the reason why the method to get a reference at a specified offset is not present. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetReference() - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return ref array!.DangerousGetReferenceAt(this.start); - } - - /// - /// Gets an instance wrapping the underlying array in use. - /// - /// An instance wrapping the underlying array in use. - /// Thrown when the buffer in use has already been disposed. - /// - /// This method is meant to be used when working with APIs that only accept an array as input, and should be used with caution. - /// In particular, the returned array is rented from an array pool, and it is responsibility of the caller to ensure that it's - /// not used after the current instance is disposed. Doing so is considered undefined behavior, - /// as the same array might be in use within another instance. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArraySegment DangerousGetArray() - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - return new ArraySegment(array!, this.start, this.length); - } - - /// - /// Slices the buffer currently in use and returns a new instance. - /// - /// The starting offset within the current buffer. - /// The length of the buffer to use. - /// A new instance using the target range of items. - /// Thrown when the buffer in use has already been disposed. - /// Thrown when or are not valid. - /// - /// Using this method will dispose the current instance, and should only be used when an oversized - /// buffer is rented and then adjusted in size, to avoid having to rent a new buffer of the new - /// size and copy the previous items into the new one, or needing an additional variable/field - /// to manually handle to track the used range within a given instance. - /// - public MemoryOwner Slice(int start, int length) - { - T[]? array = this.array; - - if (array is null) - { - ThrowObjectDisposedException(); - } - - this.array = null; - - if ((uint)start > this.length) - { - ThrowInvalidOffsetException(); - } - - if ((uint)length > (this.length - start)) - { - ThrowInvalidLengthException(); - } - - // We're transferring the ownership of the underlying array, so the current - // instance no longer needs to be disposed. Because of this, we can manually - // suppress the finalizer to reduce the overhead on the garbage collector. - GC.SuppressFinalize(this); - - return new MemoryOwner(start, length, this.pool, array!); - } - - /// - public void Dispose() - { - T[]? array = this.array; - - if (array is null) - { - return; - } - - GC.SuppressFinalize(this); - - this.array = null; - - this.pool.Return(array); - } - - /// - [Pure] - public override string ToString() - { - // Normally we would throw if the array has been disposed, - // but in this case we'll just return the non formatted - // representation as a fallback, since the ToString method - // is generally expected not to throw exceptions. - if (typeof(T) == typeof(char) && - this.array is char[] chars) - { - return new string(chars, this.start, this.length); - } - - // Same representation used in Span - return $"Microsoft.Toolkit.HighPerformance.Buffers.MemoryOwner<{typeof(T)}>[{this.length}]"; - } - - /// - /// Throws an when is . - /// - private static void ThrowObjectDisposedException() - { - throw new ObjectDisposedException(nameof(MemoryOwner), "The current buffer has already been disposed"); - } - - /// - /// Throws an when the is invalid. - /// - private static void ThrowInvalidOffsetException() - { - throw new ArgumentOutOfRangeException(nameof(start), "The input start parameter was not valid"); - } - - /// - /// Throws an when the is invalid. - /// - private static void ThrowInvalidLengthException() - { - throw new ArgumentOutOfRangeException(nameof(length), "The input length parameter was not valid"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/SpanOwner{T}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/SpanOwner{T}.cs deleted file mode 100644 index 42a354865c2..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/SpanOwner{T}.cs +++ /dev/null @@ -1,211 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if NETCORE_RUNTIME || NET5_0 -using System.Runtime.InteropServices; -#endif -using Microsoft.Toolkit.HighPerformance.Buffers.Views; - -namespace Microsoft.Toolkit.HighPerformance.Buffers -{ - /// - /// A stack-only type with the ability to rent a buffer of a specified length and getting a from it. - /// This type mirrors but without allocations and with further optimizations. - /// As this is a stack-only type, it relies on the duck-typed pattern introduced with C# 8. - /// It should be used like so: - /// - /// using (SpanOwner<byte> buffer = SpanOwner<byte>.Allocate(1024)) - /// { - /// // Use the buffer here... - /// } - /// - /// As soon as the code leaves the scope of that block, the underlying buffer will automatically - /// be disposed. The APIs in rely on this pattern for extra performance, eg. they don't perform - /// the additional checks that are done in to ensure that the buffer hasn't been disposed - /// before returning a or instance from it. - /// As such, this type should always be used with a block or expression. - /// Not doing so will cause the underlying buffer not to be returned to the shared pool. - /// - /// The type of items to store in the current instance. - [DebuggerTypeProxy(typeof(MemoryDebugView<>))] - [DebuggerDisplay("{ToString(),raw}")] - public readonly ref struct SpanOwner - { -#pragma warning disable IDE0032 - /// - /// The usable length within . - /// - private readonly int length; -#pragma warning restore IDE0032 - - /// - /// The instance used to rent . - /// - private readonly ArrayPool pool; - - /// - /// The underlying array. - /// - private readonly T[] array; - - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// The instance to use. - /// Indicates the allocation mode to use for the new buffer to rent. - private SpanOwner(int length, ArrayPool pool, AllocationMode mode) - { - this.length = length; - this.pool = pool; - this.array = pool.Rent(length); - - if (mode == AllocationMode.Clear) - { - this.array.AsSpan(0, length).Clear(); - } - } - - /// - /// Gets an empty instance. - /// - [Pure] - public static SpanOwner Empty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(0, ArrayPool.Shared, AllocationMode.Default); - } - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanOwner Allocate(int size) => new(size, ArrayPool.Shared, AllocationMode.Default); - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// The instance to use. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanOwner Allocate(int size, ArrayPool pool) => new(size, pool, AllocationMode.Default); - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// Indicates the allocation mode to use for the new buffer to rent. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanOwner Allocate(int size, AllocationMode mode) => new(size, ArrayPool.Shared, mode); - - /// - /// Creates a new instance with the specified parameters. - /// - /// The length of the new memory buffer to use. - /// The instance to use. - /// Indicates the allocation mode to use for the new buffer to rent. - /// A instance of the requested length. - /// Thrown when is not valid. - /// This method is just a proxy for the constructor, for clarity. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanOwner Allocate(int size, ArrayPool pool, AllocationMode mode) => new(size, pool, mode); - - /// - /// Gets the number of items in the current instance - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.length; - } - - /// - /// Gets a wrapping the memory belonging to the current instance. - /// - public Span Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if NETCORE_RUNTIME || NET5_0 - ref T r0 = ref array!.DangerousGetReference(); - - return MemoryMarshal.CreateSpan(ref r0, this.length); -#else - return new Span(this.array, 0, this.length); -#endif - } - } - - /// - /// Returns a reference to the first element within the current instance, with no bounds check. - /// - /// A reference to the first element within the current instance. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetReference() - { - return ref this.array.DangerousGetReference(); - } - - /// - /// Gets an instance wrapping the underlying array in use. - /// - /// An instance wrapping the underlying array in use. - /// - /// This method is meant to be used when working with APIs that only accept an array as input, and should be used with caution. - /// In particular, the returned array is rented from an array pool, and it is responsibility of the caller to ensure that it's - /// not used after the current instance is disposed. Doing so is considered undefined behavior, - /// as the same array might be in use within another instance. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArraySegment DangerousGetArray() - { - return new(array!, 0, this.length); - } - - /// - /// Implements the duck-typed method. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - this.pool.Return(this.array); - } - - /// - [Pure] - public override string ToString() - { - if (typeof(T) == typeof(char) && - this.array is char[] chars) - { - return new string(chars, 0, this.length); - } - - // Same representation used in Span - return $"Microsoft.Toolkit.HighPerformance.Buffers.SpanOwner<{typeof(T)}>[{this.length}]"; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/StringPool.cs b/Microsoft.Toolkit.HighPerformance/Buffers/StringPool.cs deleted file mode 100644 index c4b5b7e8338..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/StringPool.cs +++ /dev/null @@ -1,799 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Text; -#if !NETSTANDARD1_4 -using Microsoft.Toolkit.HighPerformance.Helpers; -#endif -using BitOperations = Microsoft.Toolkit.HighPerformance.Helpers.Internals.BitOperations; - -#nullable enable - -namespace Microsoft.Toolkit.HighPerformance.Buffers -{ - /// - /// A configurable pool for instances. This can be used to minimize allocations - /// when creating multiple instances from buffers of values. - /// The method provides a best-effort alternative to just creating - /// a new instance every time, in order to minimize the number of duplicated instances. - /// The type will internally manage a highly efficient priority queue for the - /// cached instances, so that when the full capacity is reached, the least frequently - /// used values will be automatically discarded to leave room for new values to cache. - /// - public sealed class StringPool - { - /// - /// The size used by default by the parameterless constructor. - /// - private const int DefaultSize = 2048; - - /// - /// The minimum size for instances. - /// - private const int MinimumSize = 32; - - /// - /// The current array of instances in use. - /// - private readonly FixedSizePriorityMap[] maps; - - /// - /// The total number of maps in use. - /// - private readonly int numberOfMaps; - - /// - /// Initializes a new instance of the class. - /// - public StringPool() - : this(DefaultSize) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The minimum size for the pool to create. - public StringPool(int minimumSize) - { - if (minimumSize <= 0) - { - ThrowArgumentOutOfRangeException(); - } - - // Set the minimum size - minimumSize = Math.Max(minimumSize, MinimumSize); - - // Calculates the rounded up factors for a specific size/factor pair - static void FindFactors(int size, int factor, out int x, out int y) - { - double - a = Math.Sqrt((double)size / factor), - b = factor * a; - - x = BitOperations.RoundUpPowerOfTwo((int)a); - y = BitOperations.RoundUpPowerOfTwo((int)b); - } - - // We want to find two powers of 2 factors that produce a number - // that is at least equal to the requested size. In order to find the - // combination producing the optimal factors (with the product being as - // close as possible to the requested size), we test a number of ratios - // that we consider acceptable, and pick the best results produced. - // The ratio between maps influences the number of objects being allocated, - // as well as the multithreading performance when locking on maps. - // We still want to contraint this number to avoid situations where we - // have a way too high number of maps compared to total size. - FindFactors(minimumSize, 2, out int x2, out int y2); - FindFactors(minimumSize, 3, out int x3, out int y3); - FindFactors(minimumSize, 4, out int x4, out int y4); - - int - p2 = x2 * y2, - p3 = x3 * y3, - p4 = x4 * y4; - - if (p3 < p2) - { - p2 = p3; - x2 = x3; - y2 = y3; - } - - if (p4 < p2) - { - p2 = p4; - x2 = x4; - y2 = y4; - } - - Span span = this.maps = new FixedSizePriorityMap[x2]; - - // We preallocate the maps in advance, since each bucket only contains the - // array field, which is not preinitialized, so the allocations are minimal. - // This lets us lock on each individual maps when retrieving a string instance. - foreach (ref FixedSizePriorityMap map in span) - { - map = new FixedSizePriorityMap(y2); - } - - this.numberOfMaps = x2; - - Size = p2; - } - - /// - /// Gets the shared instance. - /// - /// - /// The shared pool provides a singleton, reusable instance that - /// can be accessed directly, and that pools instances for the entire - /// process. Since is thread-safe, the shared instance can be used - /// concurrently by multiple threads without the need for manual synchronization. - /// - public static StringPool Shared { get; } = new(); - - /// - /// Gets the total number of that can be stored in the current instance. - /// - public int Size { get; } - - /// - /// Stores a instance in the internal cache. - /// - /// The input instance to cache. - public void Add(string value) - { - if (value.Length == 0) - { - return; - } - - int - hashcode = GetHashCode(value.AsSpan()), - bucketIndex = hashcode & (this.numberOfMaps - 1); - - ref FixedSizePriorityMap map = ref this.maps.DangerousGetReferenceAt(bucketIndex); - - lock (map.SyncRoot) - { - map.Add(value, hashcode); - } - } - - /// - /// Gets a cached instance matching the input content, or stores the input one. - /// - /// The input instance with the contents to use. - /// A instance with the contents of , cached if possible. - public string GetOrAdd(string value) - { - if (value.Length == 0) - { - return string.Empty; - } - - int - hashcode = GetHashCode(value.AsSpan()), - bucketIndex = hashcode & (this.numberOfMaps - 1); - - ref FixedSizePriorityMap map = ref this.maps.DangerousGetReferenceAt(bucketIndex); - - lock (map.SyncRoot) - { - return map.GetOrAdd(value, hashcode); - } - } - - /// - /// Gets a cached instance matching the input content, or creates a new one. - /// - /// The input with the contents to use. - /// A instance with the contents of , cached if possible. - public string GetOrAdd(ReadOnlySpan span) - { - if (span.IsEmpty) - { - return string.Empty; - } - - int - hashcode = GetHashCode(span), - bucketIndex = hashcode & (this.numberOfMaps - 1); - - ref FixedSizePriorityMap map = ref this.maps.DangerousGetReferenceAt(bucketIndex); - - lock (map.SyncRoot) - { - return map.GetOrAdd(span, hashcode); - } - } - - /// - /// Gets a cached instance matching the input content (converted to Unicode), or creates a new one. - /// - /// The input with the contents to use, in a specified encoding. - /// The instance to use to decode the contents of . - /// A instance with the contents of , cached if possible. - public unsafe string GetOrAdd(ReadOnlySpan span, Encoding encoding) - { - if (span.IsEmpty) - { - return string.Empty; - } - - int maxLength = encoding.GetMaxCharCount(span.Length); - - using SpanOwner buffer = SpanOwner.Allocate(maxLength); - - fixed (byte* source = span) - fixed (char* destination = &buffer.DangerousGetReference()) - { - int effectiveLength = encoding.GetChars(source, span.Length, destination, maxLength); - - return GetOrAdd(new ReadOnlySpan(destination, effectiveLength)); - } - } - - /// - /// Tries to get a cached instance matching the input content, if present. - /// - /// The input with the contents to use. - /// The resulting cached instance, if present - /// Whether or not the target instance was found. - public bool TryGet(ReadOnlySpan span, [NotNullWhen(true)] out string? value) - { - if (span.IsEmpty) - { - value = string.Empty; - - return true; - } - - int - hashcode = GetHashCode(span), - bucketIndex = hashcode & (this.numberOfMaps - 1); - - ref FixedSizePriorityMap map = ref this.maps.DangerousGetReferenceAt(bucketIndex); - - lock (map.SyncRoot) - { - return map.TryGet(span, hashcode, out value); - } - } - - /// - /// Resets the current instance and its associated maps. - /// - public void Reset() - { - foreach (ref FixedSizePriorityMap map in this.maps.AsSpan()) - { - lock (map.SyncRoot) - { - map.Reset(); - } - } - } - - /// - /// A configurable map containing a group of cached instances. - /// - /// - /// Instances of this type are stored in an array within and they are - /// always accessed by reference - essentially as if this type had been a class. The type is - /// also private, so there's no risk for users to directly access it and accidentally copy an - /// instance, which would lead to bugs due to values becoming out of sync with the internal state - /// (that is, because instances would be copied by value, so primitive fields would not be shared). - /// The reason why we're using a struct here is to remove an indirection level and improve cache - /// locality when accessing individual buckets from the methods in the type. - /// - private struct FixedSizePriorityMap - { - /// - /// The index representing the end of a given list. - /// - private const int EndOfList = -1; - - /// - /// The array of 1-based indices for the items stored in . - /// - private readonly int[] buckets; - - /// - /// The array of currently cached entries (ie. the lists for each hash group). - /// - private readonly MapEntry[] mapEntries; - - /// - /// The array of priority values associated to each item stored in . - /// - private readonly HeapEntry[] heapEntries; - - /// - /// The current number of items stored in the map. - /// - private int count; - - /// - /// The current incremental timestamp for the items stored in . - /// - private uint timestamp; - - /// - /// A type representing a map entry, ie. a node in a given list. - /// - private struct MapEntry - { - /// - /// The precomputed hashcode for . - /// - public int HashCode; - - /// - /// The instance cached in this entry. - /// - public string? Value; - - /// - /// The 0-based index for the next node in the current list. - /// - public int NextIndex; - - /// - /// The 0-based index for the heap entry corresponding to the current node. - /// - public int HeapIndex; - } - - /// - /// A type representing a heap entry, used to associate priority to each item. - /// - private struct HeapEntry - { - /// - /// The timestamp for the current entry (ie. the priority for the item). - /// - public uint Timestamp; - - /// - /// The 0-based index for the map entry corresponding to the current item. - /// - public int MapIndex; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The fixed capacity of the current map. - public FixedSizePriorityMap(int capacity) - { - this.buckets = new int[capacity]; - this.mapEntries = new MapEntry[capacity]; - this.heapEntries = new HeapEntry[capacity]; - this.count = 0; - this.timestamp = 0; - } - - /// - /// Gets an that can be used to synchronize access to the current instance. - /// - public object SyncRoot - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.buckets; - } - - /// - /// Implements for the current instance. - /// - /// The input instance to cache. - /// The precomputed hashcode for . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(string value, int hashcode) - { - ref string target = ref TryGet(value.AsSpan(), hashcode); - - if (Unsafe.IsNullRef(ref target)) - { - Insert(value, hashcode); - } - else - { - target = value; - } - } - - /// - /// Implements for the current instance. - /// - /// The input instance with the contents to use. - /// The precomputed hashcode for . - /// A instance with the contents of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string GetOrAdd(string value, int hashcode) - { - ref string result = ref TryGet(value.AsSpan(), hashcode); - - if (!Unsafe.IsNullRef(ref result)) - { - return result; - } - - Insert(value, hashcode); - - return value; - } - - /// - /// Implements for the current instance. - /// - /// The input with the contents to use. - /// The precomputed hashcode for . - /// A instance with the contents of , cached if possible. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string GetOrAdd(ReadOnlySpan span, int hashcode) - { - ref string result = ref TryGet(span, hashcode); - - if (!Unsafe.IsNullRef(ref result)) - { - return result; - } - - string value = span.ToString(); - - Insert(value, hashcode); - - return value; - } - - /// - /// Implements for the current instance. - /// - /// The input with the contents to use. - /// The precomputed hashcode for . - /// The resulting cached instance, if present - /// Whether or not the target instance was found. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGet(ReadOnlySpan span, int hashcode, [NotNullWhen(true)] out string? value) - { - ref string result = ref TryGet(span, hashcode); - - if (!Unsafe.IsNullRef(ref result)) - { - value = result; - - return true; - } - - value = null; - - return false; - } - - /// - /// Resets the current instance and throws away all the cached values. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Reset() - { - this.buckets.AsSpan().Clear(); - this.mapEntries.AsSpan().Clear(); - this.heapEntries.AsSpan().Clear(); - this.count = 0; - this.timestamp = 0; - } - - /// - /// Tries to get a target instance, if it exists, and returns a reference to it. - /// - /// The input with the contents to use. - /// The precomputed hashcode for . - /// A reference to the slot where the target instance could be. - [MethodImpl(MethodImplOptions.NoInlining)] - private unsafe ref string TryGet(ReadOnlySpan span, int hashcode) - { - ref MapEntry mapEntriesRef = ref this.mapEntries.DangerousGetReference(); - ref MapEntry entry = ref Unsafe.NullRef(); - int - length = this.buckets.Length, - bucketIndex = hashcode & (length - 1); - - for (int i = this.buckets.DangerousGetReferenceAt(bucketIndex) - 1; - (uint)i < (uint)length; - i = entry.NextIndex) - { - entry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)i); - - if (entry.HashCode == hashcode && - entry.Value!.AsSpan().SequenceEqual(span)) - { - UpdateTimestamp(ref entry.HeapIndex); - - return ref entry.Value!; - } - } - - return ref Unsafe.NullRef(); - } - - /// - /// Inserts a new instance in the current map, freeing up a space if needed. - /// - /// The new instance to store. - /// The precomputed hashcode for . - [MethodImpl(MethodImplOptions.NoInlining)] - private void Insert(string value, int hashcode) - { - ref int bucketsRef = ref this.buckets.DangerousGetReference(); - ref MapEntry mapEntriesRef = ref this.mapEntries.DangerousGetReference(); - ref HeapEntry heapEntriesRef = ref this.heapEntries.DangerousGetReference(); - int entryIndex, heapIndex; - - // If the current map is full, first get the oldest value, which is - // always the first item in the heap. Then, free up a slot by - // removing that, and insert the new value in that empty location. - if (this.count == this.mapEntries.Length) - { - entryIndex = heapEntriesRef.MapIndex; - heapIndex = 0; - - ref MapEntry removedEntry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)entryIndex); - - // The removal logic can be extremely optimized in this case, as we - // can retrieve the precomputed hashcode for the target entry by doing - // a lookup on the target map node, and we can also skip all the comparisons - // while traversing the target chain since we know in advance the index of - // the target node which will contain the item to remove from the map. - Remove(removedEntry.HashCode, entryIndex); - } - else - { - // If the free list is not empty, get that map node and update the field - entryIndex = this.count; - heapIndex = this.count; - } - - int bucketIndex = hashcode & (this.buckets.Length - 1); - ref int targetBucket = ref Unsafe.Add(ref bucketsRef, (nint)(uint)bucketIndex); - ref MapEntry targetMapEntry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)entryIndex); - ref HeapEntry targetHeapEntry = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)heapIndex); - - // Assign the values in the new map entry - targetMapEntry.HashCode = hashcode; - targetMapEntry.Value = value; - targetMapEntry.NextIndex = targetBucket - 1; - targetMapEntry.HeapIndex = heapIndex; - - // Update the bucket slot and the current count - targetBucket = entryIndex + 1; - this.count++; - - // Link the heap node with the current entry - targetHeapEntry.MapIndex = entryIndex; - - // Update the timestamp and balance the heap again - UpdateTimestamp(ref targetMapEntry.HeapIndex); - } - - /// - /// Removes a specified instance from the map to free up one slot. - /// - /// The precomputed hashcode of the instance to remove. - /// The index of the target map node to remove. - /// The input instance needs to already exist in the map. - [MethodImpl(MethodImplOptions.NoInlining)] - private void Remove(int hashcode, int mapIndex) - { - ref MapEntry mapEntriesRef = ref this.mapEntries.DangerousGetReference(); - int - bucketIndex = hashcode & (this.buckets.Length - 1), - entryIndex = this.buckets.DangerousGetReferenceAt(bucketIndex) - 1, - lastIndex = EndOfList; - - // We can just have an undefined loop, as the input - // value we're looking for is guaranteed to be present - while (true) - { - ref MapEntry candidate = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)entryIndex); - - // Check the current value for a match - if (entryIndex == mapIndex) - { - // If this was not the first list node, update the parent as well - if (lastIndex != EndOfList) - { - ref MapEntry lastEntry = ref Unsafe.Add(ref mapEntriesRef, (nint)(uint)lastIndex); - - lastEntry.NextIndex = candidate.NextIndex; - } - else - { - // Otherwise, update the target index from the bucket slot - this.buckets.DangerousGetReferenceAt(bucketIndex) = candidate.NextIndex + 1; - } - - this.count--; - - return; - } - - // Move to the following node in the current list - lastIndex = entryIndex; - entryIndex = candidate.NextIndex; - } - } - - /// - /// Updates the timestamp of a heap node at the specified index (which is then synced back). - /// - /// The index of the target heap node to update. - [MethodImpl(MethodImplOptions.NoInlining)] - private void UpdateTimestamp(ref int heapIndex) - { - int - currentIndex = heapIndex, - count = this.count; - ref MapEntry mapEntriesRef = ref this.mapEntries.DangerousGetReference(); - ref HeapEntry heapEntriesRef = ref this.heapEntries.DangerousGetReference(); - ref HeapEntry root = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)currentIndex); - uint timestamp = this.timestamp; - - // Check if incrementing the current timestamp for the heap node to update - // would result in an overflow. If that happened, we could end up violating - // the min-heap property (the value of each node has to always be <= than that - // of its child nodes), eg. if we were updating a node that was not the root. - // In that scenario, we could end up with a node somewhere along the heap with - // a value lower than that of its parent node (as the timestamp would be 0). - // To guard against this, we just check the current timestamp value, and if - // the maximum value has been reached, we reinitialize the entire heap. This - // is done in a non-inlined call, so we don't increase the codegen size in this - // method. The reinitialization simply traverses the heap in breadth-first order - // (ie. level by level), and assigns incrementing timestamps to all nodes starting - // from 0. The value of the current timestamp is then just set to the current size. - if (timestamp == uint.MaxValue) - { - // We use a goto here as this path is very rarely taken. Doing so - // causes the generated asm to contain a forward jump to the fallback - // path if this branch is taken, whereas the normal execution path will - // not need to execute any jumps at all. This is done to reduce the overhead - // introduced by this check in all the invocations where this point is not reached. - goto Fallback; - } - - Downheap: - - // Assign a new timestamp to the target heap node. We use a - // local incremental timestamp instead of using the system timer - // as this greatly reduces the overhead and the time spent in system calls. - // The uint type provides a large enough range and it's unlikely users would ever - // exhaust it anyway (especially considering each map has a separate counter). - root.Timestamp = this.timestamp = timestamp + 1; - - // Once the timestamp is updated (which will cause the heap to become - // unbalanced), start a sift down loop to balance the heap again. - while (true) - { - // The heap is 0-based (so that the array length can remain the same - // as the power of 2 value used for the other arrays in this type). - // This means that children of each node are at positions: - // - left: (2 * n) + 1 - // - right: (2 * n) + 2 - ref HeapEntry minimum = ref root; - int - left = (currentIndex * 2) + 1, - right = (currentIndex * 2) + 2, - targetIndex = currentIndex; - - // Check and update the left child, if necessary - if (left < count) - { - ref HeapEntry child = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)left); - - if (child.Timestamp < minimum.Timestamp) - { - minimum = ref child; - targetIndex = left; - } - } - - // Same check as above for the right child - if (right < count) - { - ref HeapEntry child = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)right); - - if (child.Timestamp < minimum.Timestamp) - { - minimum = ref child; - targetIndex = right; - } - } - - // If no swap is pending, we can just stop here. - // Before returning, we update the target index as well. - if (Unsafe.AreSame(ref root, ref minimum)) - { - heapIndex = targetIndex; - - return; - } - - // Update the indices in the respective map entries (accounting for the swap) - Unsafe.Add(ref mapEntriesRef, (nint)(uint)root.MapIndex).HeapIndex = targetIndex; - Unsafe.Add(ref mapEntriesRef, (nint)(uint)minimum.MapIndex).HeapIndex = currentIndex; - - currentIndex = targetIndex; - - // Swap the parent and child (so that the minimum value bubbles up) - HeapEntry temp = root; - - root = minimum; - minimum = temp; - - // Update the reference to the root node - root = ref Unsafe.Add(ref heapEntriesRef, (nint)(uint)currentIndex); - } - - Fallback: - - UpdateAllTimestamps(); - - // After having updated all the timestamps, if the heap contains N items, the - // node in the bottom right corner will have a value of N - 1. Since the timestamp - // is incremented by 1 before starting the downheap execution, here we simply - // update the local timestamp to be N - 1, so that the code above will set the - // timestamp of the node currently being updated to exactly N. - timestamp = (uint)(count - 1); - - goto Downheap; - } - - /// - /// Updates the timestamp of all the current heap nodes in incrementing order. - /// The heap is always guaranteed to be complete binary tree, so when it contains - /// a given number of nodes, those are all contiguous from the start of the array. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private void UpdateAllTimestamps() - { - int count = this.count; - ref HeapEntry heapEntriesRef = ref this.heapEntries.DangerousGetReference(); - - for (int i = 0; i < count; i++) - { - Unsafe.Add(ref heapEntriesRef, (nint)(uint)i).Timestamp = (uint)i; - } - } - } - - /// - /// Gets the (positive) hashcode for a given instance. - /// - /// The input instance. - /// The hashcode for . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int GetHashCode(ReadOnlySpan span) - { -#if NETSTANDARD1_4 - return span.GetDjb2HashCode(); -#else - return HashCode.Combine(span); -#endif - } - - /// - /// Throws an when the requested size exceeds the capacity. - /// - private static void ThrowArgumentOutOfRangeException() - { - throw new ArgumentOutOfRangeException("minimumSize", "The requested size must be greater than 0"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Buffers/Views/MemoryDebugView{T}.cs b/Microsoft.Toolkit.HighPerformance/Buffers/Views/MemoryDebugView{T}.cs deleted file mode 100644 index b273a8a4839..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Buffers/Views/MemoryDebugView{T}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; - -namespace Microsoft.Toolkit.HighPerformance.Buffers.Views -{ - /// - /// A debug proxy used to display items in a 1D layout. - /// - /// The type of items to display. - internal sealed class MemoryDebugView - { - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView(ArrayPoolBufferWriter? arrayPoolBufferWriter) - { - this.Items = arrayPoolBufferWriter?.WrittenSpan.ToArray(); - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView(MemoryBufferWriter? memoryBufferWriter) - { - this.Items = memoryBufferWriter?.WrittenSpan.ToArray(); - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView(MemoryOwner? memoryOwner) - { - this.Items = memoryOwner?.Span.ToArray(); - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView(SpanOwner spanOwner) - { - this.Items = spanOwner.Span.ToArray(); - } - - /// - /// Gets the items to display for the current instance - /// - [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] - public T[]? Items { get; } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs deleted file mode 100644 index aa4507fafe8..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable{T}.cs +++ /dev/null @@ -1,472 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -#endif -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -#if !SPAN_RUNTIME_SUPPORT -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; -#endif - -namespace Microsoft.Toolkit.HighPerformance.Enumerables -{ - /// - /// A that iterates readonly items from arbitrary memory locations. - /// - /// The type of items to enumerate. - public readonly ref struct ReadOnlyRefEnumerable - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance pointing to the first item in the target memory area. - /// - /// The field maps to the total available length. - private readonly ReadOnlySpan span; -#else - /// - /// The target instance, if present. - /// - private readonly object? instance; - - /// - /// The initial offset within . - /// - private readonly IntPtr offset; - - /// - /// The total available length for the sequence. - /// - private readonly int length; -#endif - - /// - /// The distance between items in the sequence to enumerate. - /// - /// The distance refers to items, not byte offset. - private readonly int step; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The instance pointing to the first item in the target memory area. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ReadOnlyRefEnumerable(ReadOnlySpan span, int step) - { - this.span = span; - this.step = step; - } - - /// - /// Initializes a new instance of the struct. - /// - /// A reference to the first item of the sequence. - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlyRefEnumerable(in T reference, int length, int step) - { - this.span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(reference), length); - this.step = step; - } - - /// - /// Creates a new instance of the struct with the specified parameters. - /// - /// The reference to the first item to map. - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - /// A instance with the specified parameters. - /// Thrown when one of the parameters are negative. - [Pure] - public static ReadOnlyRefEnumerable DangerousCreate(in T value, int length, int step) - { - if (length < 0) - { - ThrowArgumentOutOfRangeExceptionForLength(); - } - - if (step < 0) - { - ThrowArgumentOutOfRangeExceptionForStep(); - } - - OverflowHelper.EnsureIsInNativeIntRange(length, 1, step); - - return new ReadOnlyRefEnumerable(in value, length, step); - } -#else - /// - /// Initializes a new instance of the struct. - /// - /// The target instance. - /// The initial offset within . - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlyRefEnumerable(object? instance, IntPtr offset, int length, int step) - { - this.instance = instance; - this.offset = offset; - this.length = length; - this.step = step; - } -#endif - - /// - /// Gets the total available length for the sequence. - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if SPAN_RUNTIME_SUPPORT - get => this.span.Length; -#else - get => this.length; -#endif - } - - /// - /// Gets the element at the specified zero-based index. - /// - /// The zero-based index of the element. - /// A reference to the element at the specified index. - /// - /// Thrown when is invalid. - /// - public ref readonly T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if ((uint)index >= (uint)Length) - { - ThrowHelper.ThrowIndexOutOfRangeException(); - } - -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - nint offset = (nint)(uint)index * (nint)(uint)this.step; - ref T ri = ref Unsafe.Add(ref r0, offset); - - return ref ri; - } - } - -#if NETSTANDARD2_1_OR_GREATER - /// - /// Gets the element at the specified zero-based index. - /// - /// The zero-based index of the element. - /// A reference to the element at the specified index. - /// - /// Thrown when is invalid. - /// - public ref readonly T this[Index index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref this[index.GetOffset(Length)]; - } -#endif - - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() - { -#if SPAN_RUNTIME_SUPPORT - return new Enumerator(this.span, this.step); -#else - return new Enumerator(this.instance, this.offset, this.length, this.step); -#endif - } - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(RefEnumerable destination) - { -#if SPAN_RUNTIME_SUPPORT - if (this.step == 1) - { - destination.CopyFrom(this.span); - - return; - } - - if (destination.Step == 1) - { - CopyTo(destination.Span); - - return; - } - - ref T sourceRef = ref this.span.DangerousGetReference(); - ref T destinationRef = ref destination.Span.DangerousGetReference(); - int - sourceLength = this.span.Length, - destinationLength = destination.Span.Length; -#else - ref T sourceRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); - ref T destinationRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(destination.Instance, destination.Offset); - int - sourceLength = this.length, - destinationLength = destination.Length; -#endif - - if ((uint)destinationLength < (uint)sourceLength) - { - ThrowArgumentExceptionForDestinationTooShort(); - } - - RefEnumerableHelper.CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)sourceLength, (nint)(uint)this.step, (nint)(uint)destination.Step); - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(RefEnumerable destination) - { -#if SPAN_RUNTIME_SUPPORT - int - sourceLength = this.span.Length, - destinationLength = destination.Span.Length; -#else - int - sourceLength = this.length, - destinationLength = destination.Length; -#endif - - if (destinationLength >= sourceLength) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Span destination) - { -#if SPAN_RUNTIME_SUPPORT - if (this.step == 1) - { - this.span.CopyTo(destination); - - return; - } - - ref T sourceRef = ref this.span.DangerousGetReference(); - int length = this.span.Length; -#else - ref T sourceRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); - int length = this.length; -#endif - if ((uint)destination.Length < (uint)length) - { - ThrowArgumentExceptionForDestinationTooShort(); - } - - ref T destinationRef = ref destination.DangerousGetReference(); - - RefEnumerableHelper.CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)length, (nint)(uint)this.step); - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Span destination) - { -#if SPAN_RUNTIME_SUPPORT - int length = this.span.Length; -#else - int length = this.length; -#endif - - if (destination.Length >= length) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - [Pure] - public T[] ToArray() - { -#if SPAN_RUNTIME_SUPPORT - int length = this.span.Length; -#else - int length = this.length; -#endif - - // Empty array if no data is mapped - if (length == 0) - { - return Array.Empty(); - } - - T[] array = new T[length]; - - CopyTo(array); - - return array; - } - - /// - /// Implicitly converts a instance into a one. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlyRefEnumerable(RefEnumerable enumerable) - { -#if SPAN_RUNTIME_SUPPORT - return new ReadOnlyRefEnumerable(enumerable.Span, enumerable.Step); -#else - return new ReadOnlyRefEnumerable(enumerable.Instance, enumerable.Offset, enumerable.Length, enumerable.Step); -#endif - } - - /// - /// A custom enumerator type to traverse items within a instance. - /// - public ref struct Enumerator - { -#if SPAN_RUNTIME_SUPPORT - /// - private readonly ReadOnlySpan span; -#else - /// - private readonly object? instance; - - /// - private readonly IntPtr offset; - - /// - private readonly int length; -#endif - - /// - private readonly int step; - - /// - /// The current position in the sequence. - /// - private int position; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The instance with the info on the items to traverse. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(ReadOnlySpan span, int step) - { - this.span = span; - this.step = step; - this.position = -1; - } -#else - /// - /// Initializes a new instance of the struct. - /// - /// The target instance. - /// The initial offset within . - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(object? instance, IntPtr offset, int length, int step) - { - this.instance = instance; - this.offset = offset; - this.length = length; - this.step = step; - this.position = -1; - } -#endif - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { -#if SPAN_RUNTIME_SUPPORT - return ++this.position < this.span.Length; -#else - return ++this.position < this.length; -#endif - } - - /// - public readonly ref readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref this.span.DangerousGetReference(); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - nint offset = (nint)(uint)this.position * (nint)(uint)this.step; - ref T ri = ref Unsafe.Add(ref r0, offset); - - return ref ri; - } - } - } - - /// - /// Throws an when the "length" parameter is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForLength() - { - throw new ArgumentOutOfRangeException("length"); - } - - /// - /// Throws an when the "step" parameter is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForStep() - { - throw new ArgumentOutOfRangeException("step"); - } - - /// - /// Throws an when the target span is too short. - /// - private static void ThrowArgumentExceptionForDestinationTooShort() - { - throw new ArgumentException("The target span is too short to copy all the current items to"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs deleted file mode 100644 index e06d23f16e8..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanEnumerable{T}.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.HighPerformance.Enumerables -{ - /// - /// A that enumerates the items in a given instance. - /// - /// The type of items to enumerate. - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct ReadOnlySpanEnumerable - { - /// - /// The source instance. - /// - private readonly ReadOnlySpan span; - - /// - /// The current index within . - /// - private int index; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpanEnumerable(ReadOnlySpan span) - { - this.span = span; - this.index = -1; - } - - /// - /// Implements the duck-typed method. - /// - /// An instance targeting the current value. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly ReadOnlySpanEnumerable GetEnumerator() => this; - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - return ++this.index < this.span.Length; - } - - /// - /// Gets the duck-typed property. - /// - public readonly Item Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)this.index); - - // See comment in SpanEnumerable about this - return new Item(ref ri, this.index); -#else - return new Item(this.span, this.index); -#endif - } - } - - /// - /// An item from a source instance. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct Item - { - /// - /// The source instance. - /// - private readonly ReadOnlySpan span; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// A reference to the target value. - /// The index of the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Item(ref T value, int index) - { - this.span = MemoryMarshal.CreateReadOnlySpan(ref value, index); - } -#else - /// - /// The current index within . - /// - private readonly int index; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - /// The current index within . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Item(ReadOnlySpan span, int index) - { - this.span = span; - this.index = index; - } -#endif - - /// - /// Gets the reference to the current value. - /// - public ref readonly T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return ref MemoryMarshal.GetReference(this.span); -#else - ref T r0 = ref MemoryMarshal.GetReference(this.span); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)this.index); - - return ref ri; -#endif - } - } - - /// - /// Gets the current index. - /// - public int Index - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return this.span.Length; -#else - return this.index; -#endif - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanTokenizer{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanTokenizer{T}.cs deleted file mode 100644 index f4d3e543614..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/ReadOnlySpanTokenizer{T}.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Enumerables -{ - /// - /// A that tokenizes a given instance. - /// - /// The type of items to enumerate. - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct ReadOnlySpanTokenizer - where T : IEquatable - { - /// - /// The source instance. - /// - private readonly ReadOnlySpan span; - - /// - /// The separator item to use. - /// - private readonly T separator; - - /// - /// The current initial offset. - /// - private int start; - - /// - /// The current final offset. - /// - private int end; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - /// The separator item to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpanTokenizer(ReadOnlySpan span, T separator) - { - this.span = span; - this.separator = separator; - this.start = 0; - this.end = -1; - } - - /// - /// Implements the duck-typed method. - /// - /// An instance targeting the current value. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly ReadOnlySpanTokenizer GetEnumerator() => this; - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int - newEnd = this.end + 1, - length = this.span.Length; - - // Additional check if the separator is not the last character - if (newEnd <= length) - { - this.start = newEnd; - - // We need to call this extension explicitly or the extension method resolution rules for the C# compiler - // will end up picking Microsoft.Toolkit.HighPerformance.ReadOnlySpanExtensions.IndexOf instead, even - // though the latter takes the parameter via a readonly reference. This is because the "in" modifier is - // implicit, which makes the signature compatible, and because extension methods are matched in such a - // way that methods "closest" to where they're used are preferred. Since this type shares the same root - // namespace, this makes that extension a better match, so that it overrides the MemoryExtensions one. - // This is not a problem for consumers of this package, as their code would be outside of the - // Microsoft.Toolkit.HighPerformance namespace, so both extensions would be "equally distant", so that - // when they're both in scope it will be possible to choose which one to use by adding an explicit "in". - int index = System.MemoryExtensions.IndexOf(this.span.Slice(newEnd), this.separator); - - // Extract the current subsequence - if (index >= 0) - { - this.end = newEnd + index; - - return true; - } - - this.end = length; - - return true; - } - - return false; - } - - /// - /// Gets the duck-typed property. - /// - public readonly ReadOnlySpan Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.span.Slice(this.start, this.end - this.start); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/RefEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/RefEnumerable{T}.cs deleted file mode 100644 index 535b742befa..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/RefEnumerable{T}.cs +++ /dev/null @@ -1,560 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -#endif -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -#if !SPAN_RUNTIME_SUPPORT -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; -#endif - -namespace Microsoft.Toolkit.HighPerformance.Enumerables -{ - /// - /// A that iterates items from arbitrary memory locations. - /// - /// The type of items to enumerate. - public readonly ref struct RefEnumerable - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance pointing to the first item in the target memory area. - /// - /// The field maps to the total available length. - internal readonly Span Span; -#else - /// - /// The target instance, if present. - /// - internal readonly object? Instance; - - /// - /// The initial offset within . - /// - internal readonly IntPtr Offset; -#endif - - /// - /// The distance between items in the sequence to enumerate. - /// - /// The distance refers to items, not byte offset. - internal readonly int Step; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// A reference to the first item of the sequence. - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal RefEnumerable(ref T reference, int length, int step) - { - Span = MemoryMarshal.CreateSpan(ref reference, length); - Step = step; - } - - /// - /// Creates a new instance of the struct with the specified parameters. - /// - /// The reference to the first item to map. - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - /// A instance with the specified parameters. - /// Thrown when one of the parameters are negative. - [Pure] - public static RefEnumerable DangerousCreate(ref T value, int length, int step) - { - if (length < 0) - { - ThrowArgumentOutOfRangeExceptionForLength(); - } - - if (step < 0) - { - ThrowArgumentOutOfRangeExceptionForStep(); - } - - OverflowHelper.EnsureIsInNativeIntRange(length, 1, step); - - return new RefEnumerable(ref value, length, step); - } -#else - /// - /// Initializes a new instance of the struct. - /// - /// The target instance. - /// The initial offset within . - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal RefEnumerable(object? instance, IntPtr offset, int length, int step) - { - Instance = instance; - Offset = offset; - Length = length; - Step = step; - } -#endif - - /// - /// Gets the total available length for the sequence. - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#if SPAN_RUNTIME_SUPPORT - get => this.Span.Length; -#else - get; -#endif - } - - /// - /// Gets the element at the specified zero-based index. - /// - /// The zero-based index of the element. - /// A reference to the element at the specified index. - /// - /// Thrown when is invalid. - /// - public ref T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if ((uint)index >= (uint)Length) - { - ThrowHelper.ThrowIndexOutOfRangeException(); - } - -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.Span); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); -#endif - nint offset = (nint)(uint)index * (nint)(uint)this.Step; - ref T ri = ref Unsafe.Add(ref r0, offset); - - return ref ri; - } - } - -#if NETSTANDARD2_1_OR_GREATER - /// - /// Gets the element at the specified zero-based index. - /// - /// The zero-based index of the element. - /// A reference to the element at the specified index. - /// - /// Thrown when is invalid. - /// - public ref T this[Index index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref this[index.GetOffset(Length)]; - } -#endif - - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() - { -#if SPAN_RUNTIME_SUPPORT - return new Enumerator(this.Span, this.Step); -#else - return new Enumerator(this.Instance, this.Offset, this.Length, this.Step); -#endif - } - - /// - /// Clears the contents of the current instance. - /// - public void Clear() - { -#if SPAN_RUNTIME_SUPPORT - // Fast path for contiguous items - if (this.Step == 1) - { - this.Span.Clear(); - - return; - } - - ref T r0 = ref this.Span.DangerousGetReference(); - int length = this.Span.Length; -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); - int length = this.Length; -#endif - - RefEnumerableHelper.Clear(ref r0, (nint)(uint)length, (nint)(uint)this.Step); - } - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(RefEnumerable destination) - { -#if SPAN_RUNTIME_SUPPORT - if (this.Step == 1) - { - destination.CopyFrom(this.Span); - - return; - } - - if (destination.Step == 1) - { - CopyTo(destination.Span); - - return; - } - - ref T sourceRef = ref this.Span.DangerousGetReference(); - ref T destinationRef = ref destination.Span.DangerousGetReference(); - int - sourceLength = this.Span.Length, - destinationLength = destination.Span.Length; -#else - ref T sourceRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); - ref T destinationRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(destination.Instance, destination.Offset); - int - sourceLength = this.Length, - destinationLength = destination.Length; -#endif - - if ((uint)destinationLength < (uint)sourceLength) - { - ThrowArgumentExceptionForDestinationTooShort(); - } - - RefEnumerableHelper.CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)sourceLength, (nint)(uint)this.Step, (nint)(uint)destination.Step); - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(RefEnumerable destination) - { -#if SPAN_RUNTIME_SUPPORT - int - sourceLength = this.Span.Length, - destinationLength = destination.Span.Length; -#else - int - sourceLength = this.Length, - destinationLength = destination.Length; -#endif - - if (destinationLength >= sourceLength) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Span destination) - { -#if SPAN_RUNTIME_SUPPORT - if (this.Step == 1) - { - this.Span.CopyTo(destination); - - return; - } - - ref T sourceRef = ref this.Span.DangerousGetReference(); - int length = this.Span.Length; -#else - ref T sourceRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); - int length = this.Length; -#endif - if ((uint)destination.Length < (uint)length) - { - ThrowArgumentExceptionForDestinationTooShort(); - } - - ref T destinationRef = ref destination.DangerousGetReference(); - - RefEnumerableHelper.CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)length, (nint)(uint)this.Step); - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Span destination) - { -#if SPAN_RUNTIME_SUPPORT - int length = this.Span.Length; -#else - int length = this.Length; -#endif - - if (destination.Length >= length) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - /// Copies the contents of a source into the current instance. - /// - /// The source instance. - /// - /// Thrown when the current is shorter than the source instance. - /// - internal void CopyFrom(ReadOnlySpan source) - { -#if SPAN_RUNTIME_SUPPORT - if (this.Step == 1) - { - source.CopyTo(this.Span); - - return; - } - - ref T destinationRef = ref this.Span.DangerousGetReference(); - int destinationLength = this.Span.Length; -#else - ref T destinationRef = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); - int destinationLength = this.Length; -#endif - ref T sourceRef = ref source.DangerousGetReference(); - int sourceLength = source.Length; - - if ((uint)destinationLength < (uint)sourceLength) - { - ThrowArgumentExceptionForDestinationTooShort(); - } - - RefEnumerableHelper.CopyFrom(ref sourceRef, ref destinationRef, (nint)(uint)sourceLength, (nint)(uint)this.Step); - } - - /// - /// Attempts to copy the source into the current instance. - /// - /// The source instance. - /// Whether or not the operation was successful. - public bool TryCopyFrom(ReadOnlySpan source) - { -#if SPAN_RUNTIME_SUPPORT - int length = this.Span.Length; -#else - int length = this.Length; -#endif - - if (length >= source.Length) - { - CopyFrom(source); - - return true; - } - - return false; - } - - /// - /// Fills the elements of this with a specified value. - /// - /// The value to assign to each element of the instance. - public void Fill(T value) - { -#if SPAN_RUNTIME_SUPPORT - if (this.Step == 1) - { - this.Span.Fill(value); - - return; - } - - ref T r0 = ref this.Span.DangerousGetReference(); - int length = this.Span.Length; -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); - int length = this.Length; -#endif - - RefEnumerableHelper.Fill(ref r0, (nint)(uint)length, (nint)(uint)this.Step, value); - } - - /// - /// Returns a array with the values in the target row. - /// - /// A array with the values in the target row. - /// - /// This method will allocate a new array, so only - /// use it if you really need to copy the target items in a new memory location. - /// - [Pure] - public T[] ToArray() - { -#if SPAN_RUNTIME_SUPPORT - int length = this.Span.Length; -#else - int length = this.Length; -#endif - - // Empty array if no data is mapped - if (length == 0) - { - return Array.Empty(); - } - - T[] array = new T[length]; - - CopyTo(array); - - return array; - } - - /// - /// A custom enumerator type to traverse items within a instance. - /// - public ref struct Enumerator - { -#if SPAN_RUNTIME_SUPPORT - /// - private readonly Span span; -#else - /// - private readonly object? instance; - - /// - private readonly IntPtr offset; - - /// - private readonly int length; -#endif - - /// - private readonly int step; - - /// - /// The current position in the sequence. - /// - private int position; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The instance with the info on the items to traverse. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(Span span, int step) - { - this.span = span; - this.step = step; - this.position = -1; - } -#else - /// - /// Initializes a new instance of the struct. - /// - /// The target instance. - /// The initial offset within . - /// The number of items in the sequence. - /// The distance between items in the sequence to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(object? instance, IntPtr offset, int length, int step) - { - this.instance = instance; - this.offset = offset; - this.length = length; - this.step = step; - this.position = -1; - } -#endif - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { -#if SPAN_RUNTIME_SUPPORT - return ++this.position < this.span.Length; -#else - return ++this.position < this.length; -#endif - } - - /// - public readonly ref T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref this.span.DangerousGetReference(); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - - // Here we just offset by shifting down as if we were traversing a 2D array with a - // a single column, with the width of each row represented by the step, the height - // represented by the current position, and with only the first element of each row - // being inspected. We can perform all the indexing operations in this type as nint, - // as the maximum offset is guaranteed never to exceed the maximum value, since on - // 32 bit architectures it's not possible to allocate that much memory anyway. - nint offset = (nint)(uint)this.position * (nint)(uint)this.step; - ref T ri = ref Unsafe.Add(ref r0, offset); - - return ref ri; - } - } - } - - /// - /// Throws an when the "length" parameter is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForLength() - { - throw new ArgumentOutOfRangeException("length"); - } - - /// - /// Throws an when the "step" parameter is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForStep() - { - throw new ArgumentOutOfRangeException("step"); - } - - /// - /// Throws an when the target span is too short. - /// - private static void ThrowArgumentExceptionForDestinationTooShort() - { - throw new ArgumentException("The target span is too short to copy all the current items to"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs deleted file mode 100644 index 059e7d5d7e4..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanEnumerable{T}.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.HighPerformance.Enumerables -{ - /// - /// A that enumerates the items in a given instance. - /// - /// The type of items to enumerate. - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct SpanEnumerable - { - /// - /// The source instance. - /// - private readonly Span span; - - /// - /// The current index within . - /// - private int index; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SpanEnumerable(Span span) - { - this.span = span; - this.index = -1; - } - - /// - /// Implements the duck-typed method. - /// - /// An instance targeting the current value. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly SpanEnumerable GetEnumerator() => this; - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - return ++this.index < this.span.Length; - } - - /// - /// Gets the duck-typed property. - /// - public readonly Item Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)this.index); - - // On .NET Standard 2.1 and .NET Core (or on any target that offers runtime - // support for the Span 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-s from arbitrary references. - return new Item(ref ri, this.index); -#else - return new Item(this.span, this.index); -#endif - } - } - - /// - /// An item from a source instance. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct Item - { - /// - /// The source instance. - /// - private readonly Span span; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// A reference to the target value. - /// The index of the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Item(ref T value, int index) - { - this.span = MemoryMarshal.CreateSpan(ref value, index); - } -#else - /// - /// The current index within . - /// - private readonly int index; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - /// The current index within . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Item(Span span, int index) - { - this.span = span; - this.index = index; - } -#endif - - /// - /// Gets the reference to the current value. - /// - public ref T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return ref MemoryMarshal.GetReference(this.span); -#else - ref T r0 = ref MemoryMarshal.GetReference(this.span); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)this.index); - - return ref ri; -#endif - } - } - - /// - /// Gets the current index. - /// - public int Index - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return this.span.Length; -#else - return this.index; -#endif - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanTokenizer{T}.cs b/Microsoft.Toolkit.HighPerformance/Enumerables/SpanTokenizer{T}.cs deleted file mode 100644 index d795c9e4cc6..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Enumerables/SpanTokenizer{T}.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Enumerables -{ - /// - /// A that tokenizes a given instance. - /// - /// The type of items to enumerate. - [EditorBrowsable(EditorBrowsableState.Never)] - public ref struct SpanTokenizer - where T : IEquatable - { - /// - /// The source instance. - /// - private readonly Span span; - - /// - /// The separator item to use. - /// - private readonly T separator; - - /// - /// The current initial offset. - /// - private int start; - - /// - /// The current final offset. - /// - private int end; - - /// - /// Initializes a new instance of the struct. - /// - /// The source instance. - /// The separator item to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SpanTokenizer(Span span, T separator) - { - this.span = span; - this.separator = separator; - this.start = 0; - this.end = -1; - } - - /// - /// Implements the duck-typed method. - /// - /// An instance targeting the current value. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly SpanTokenizer GetEnumerator() => this; - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int - newEnd = this.end + 1, - length = this.span.Length; - - // Additional check if the separator is not the last character - if (newEnd <= length) - { - this.start = newEnd; - - int index = this.span.Slice(newEnd).IndexOf(this.separator); - - // Extract the current subsequence - if (index >= 0) - { - this.end = newEnd + index; - - return true; - } - - this.end = length; - - return true; - } - - return false; - } - - /// - /// Gets the duck-typed property. - /// - public readonly Span Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.span.Slice(this.start, this.end - this.start); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.1D.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.1D.cs deleted file mode 100644 index c4d845ef6ec..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.1D.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if NETCORE_RUNTIME || NET5_0 -using System.Runtime.InteropServices; -#endif -using Microsoft.Toolkit.HighPerformance.Enumerables; -#if !NETCORE_RUNTIME && !NET5_0 -using Microsoft.Toolkit.HighPerformance.Helpers; -#endif -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static partial class ArrayExtensions - { - /// - /// Returns a reference to the first element within a given array, with no bounds checks. - /// - /// The type of elements in the input array instance. - /// The input array instance. - /// A reference to the first element within , or the location it would have used, if is empty. - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReference(this T[] array) - { -#if NET5_0 - return ref MemoryMarshal.GetArrayDataReference(array); -#elif NETCORE_RUNTIME - var arrayData = Unsafe.As(array)!; - ref T r0 = ref Unsafe.As(ref arrayData.Data); - - return ref r0; -#else - IntPtr offset = RuntimeHelpers.GetArrayDataByteOffset(); - - return ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, offset); -#endif - } - - /// - /// Returns a reference to an element at a specified index within a given array, with no bounds checks. - /// - /// The type of elements in the input array instance. - /// The input array instance. - /// The index of the element to retrieve within . - /// A reference to the element within at the index specified by . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReferenceAt(this T[] array, int i) - { -#if NET5_0 - ref T r0 = ref MemoryMarshal.GetArrayDataReference(array); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i); - - return ref ri; -#elif NETCORE_RUNTIME - var arrayData = Unsafe.As(array)!; - ref T r0 = ref Unsafe.As(ref arrayData.Data); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i); - - return ref ri; -#else - IntPtr offset = RuntimeHelpers.GetArrayDataByteOffset(); - ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, offset); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i); - - return ref ri; -#endif - } - -#if NETCORE_RUNTIME - // Description taken from CoreCLR: see https://source.dot.net/#System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs,285. - // CLR arrays are laid out in memory as follows (multidimensional array bounds are optional): - // [ sync block || pMethodTable || num components || MD array bounds || array data .. ] - // ^ ^ ^ returned reference - // | \-- ref Unsafe.As(array).Data - // \-- array - // The base size of an array includes all the fields before the array data, - // including the sync block and method table. The reference to RawData.Data - // points at the number of components, skipping over these two pointer-sized fields. - [StructLayout(LayoutKind.Sequential)] - private sealed class RawArrayData - { -#pragma warning disable CS0649 // Unassigned fields -#pragma warning disable SA1401 // Fields should be private - public IntPtr Length; - public byte Data; -#pragma warning restore CS0649 -#pragma warning restore SA1401 - } -#endif - - /// - /// Counts the number of occurrences of a given value into a target array instance. - /// - /// The type of items in the input array instance. - /// The input array instance. - /// The value to look for. - /// The number of occurrences of in . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Count(this T[] array, T value) - where T : IEquatable - { - ref T r0 = ref array.DangerousGetReference(); - nint - length = RuntimeHelpers.GetArrayNativeLength(array), - count = SpanHelper.Count(ref r0, length, value); - - if ((nuint)count > int.MaxValue) - { - ThrowOverflowException(); - } - - return (int)count; - } - - /// - /// Enumerates the items in the input array instance, as pairs of reference/index values. - /// This extension should be used directly within a loop: - /// - /// int[] numbers = new[] { 1, 2, 3, 4, 5, 6, 7 }; - /// - /// foreach (var item in numbers.Enumerate()) - /// { - /// // Access the index and value of each item here... - /// int index = item.Index; - /// ref int value = ref item.Value; - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The type of items to enumerate. - /// The source array to enumerate. - /// A wrapper type that will handle the reference/index enumeration for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanEnumerable Enumerate(this T[] array) - { - return new(array); - } - - /// - /// Tokenizes the values in the input array instance using a specified separator. - /// This extension should be used directly within a loop: - /// - /// char[] text = "Hello, world!".ToCharArray(); - /// - /// foreach (var token in text.Tokenize(',')) - /// { - /// // Access the tokens here... - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The type of items in the array to tokenize. - /// The source array to tokenize. - /// The separator item to use. - /// A wrapper type that will handle the tokenization for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanTokenizer Tokenize(this T[] array, T separator) - where T : IEquatable - { - return new(array, separator); - } - - /// - /// Gets a content hash from the input array instance using the Djb2 algorithm. - /// For more info, see the documentation for . - /// - /// The type of items in the input array instance. - /// The input array instance. - /// The Djb2 value for the input array instance. - /// The Djb2 hash is fully deterministic and with no random components. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetDjb2HashCode(this T[] array) - where T : notnull - { - ref T r0 = ref array.DangerousGetReference(); - nint length = RuntimeHelpers.GetArrayNativeLength(array); - - return SpanHelper.GetDjb2HashCode(ref r0, length); - } - - /// - /// Checks whether or not a given array is covariant. - /// - /// The type of items in the input array instance. - /// The input array instance. - /// Whether or not is covariant. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsCovariant(this T[] array) - { - return default(T) is null && array.GetType() != typeof(T[]); - } - - /// - /// Throws an when the "column" parameter is invalid. - /// - private static void ThrowOverflowException() - { - throw new OverflowException(); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.2D.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.2D.cs deleted file mode 100644 index 4ac006aac20..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.2D.cs +++ /dev/null @@ -1,467 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Internals; -#endif -using Microsoft.Toolkit.HighPerformance.Enumerables; -using Microsoft.Toolkit.HighPerformance.Helpers; -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static partial class ArrayExtensions - { - /// - /// Returns a reference to the first element within a given 2D array, with no bounds checks. - /// - /// The type of elements in the input 2D array instance. - /// The input array instance. - /// A reference to the first element within , or the location it would have used, if is empty. - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReference(this T[,] array) - { -#if NETCORE_RUNTIME - var arrayData = Unsafe.As(array)!; - ref T r0 = ref Unsafe.As(ref arrayData.Data); - - return ref r0; -#else - IntPtr offset = RuntimeHelpers.GetArray2DDataByteOffset(); - - return ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, offset); -#endif - } - - /// - /// Returns a reference to an element at a specified coordinate within a given 2D array, with no bounds checks. - /// - /// The type of elements in the input 2D array instance. - /// The input 2D array instance. - /// The vertical index of the element to retrieve within . - /// The horizontal index of the element to retrieve within . - /// A reference to the element within at the coordinate specified by and . - /// - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the - /// and parameters are valid. Furthermore, this extension will ignore the lower bounds for the input - /// array, and will just assume that the input index is 0-based. It is responsibility of the caller to adjust the input - /// indices to account for the actual lower bounds, if the input array has either axis not starting at 0. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReferenceAt(this T[,] array, int i, int j) - { -#if NETCORE_RUNTIME - var arrayData = Unsafe.As(array)!; - nint offset = ((nint)(uint)i * (nint)(uint)arrayData.Width) + (nint)(uint)j; - ref T r0 = ref Unsafe.As(ref arrayData.Data); - ref T ri = ref Unsafe.Add(ref r0, offset); - - return ref ri; -#else - int width = array.GetLength(1); - nint index = ((nint)(uint)i * (nint)(uint)width) + (nint)(uint)j; - IntPtr offset = RuntimeHelpers.GetArray2DDataByteOffset(); - ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, offset); - ref T ri = ref Unsafe.Add(ref r0, index); - - return ref ri; -#endif - } - -#if NETCORE_RUNTIME - // Description adapted from CoreCLR: see https://source.dot.net/#System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs,285. - // CLR 2D arrays are laid out in memory as follows: - // [ sync block || pMethodTable || Length (padded to IntPtr) || HxW || HxW bounds || array data .. ] - // ^ ^ - // | \-- ref Unsafe.As(array).Data - // \-- array - // The length is always padded to IntPtr just like with SZ arrays. - // The total data padding is therefore 20 bytes on x86 (4 + 4 + 4 + 4 + 4), or 24 bytes on x64. - [StructLayout(LayoutKind.Sequential)] - private sealed class RawArray2DData - { -#pragma warning disable CS0649 // Unassigned fields -#pragma warning disable SA1401 // Fields should be private - public IntPtr Length; - public int Height; - public int Width; - public int HeightLowerBound; - public int WidthLowerBound; - public byte Data; -#pragma warning restore CS0649 -#pragma warning restore SA1401 - } -#endif - - /// - /// Returns a over a row in a given 2D array instance. - /// - /// The type of elements in the input 2D array instance. - /// The input array instance. - /// The target row to retrieve (0-based index). - /// A with the items from the target row within . - /// The returned value shouldn't be used directly: use this extension in a loop. - /// Thrown when one of the input parameters is out of range. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RefEnumerable GetRow(this T[,] array, int row) - { - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - int height = array.GetLength(0); - - if ((uint)row >= (uint)height) - { - ThrowArgumentOutOfRangeExceptionForRow(); - } - - int width = array.GetLength(1); - -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref array.DangerousGetReferenceAt(row, 0); - - return new RefEnumerable(ref r0, width, 1); -#else - ref T r0 = ref array.DangerousGetReferenceAt(row, 0); - IntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref r0); - - return new RefEnumerable(array, offset, width, 1); -#endif - } - - /// - /// Returns a that returns the items from a given column in a given 2D array instance. - /// This extension should be used directly within a loop: - /// - /// int[,] matrix = - /// { - /// { 1, 2, 3 }, - /// { 4, 5, 6 }, - /// { 7, 8, 9 } - /// }; - /// - /// foreach (ref int number in matrix.GetColumn(1)) - /// { - /// // Access the current number by reference here... - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The type of elements in the input 2D array instance. - /// The input array instance. - /// The target column to retrieve (0-based index). - /// A wrapper type that will handle the column enumeration for . - /// The returned value shouldn't be used directly: use this extension in a loop. - /// Thrown when one of the input parameters is out of range. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RefEnumerable GetColumn(this T[,] array, int column) - { - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - int width = array.GetLength(1); - - if ((uint)column >= (uint)width) - { - ThrowArgumentOutOfRangeExceptionForColumn(); - } - - int height = array.GetLength(0); - -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref array.DangerousGetReferenceAt(0, column); - - return new RefEnumerable(ref r0, height, width); -#else - ref T r0 = ref array.DangerousGetReferenceAt(0, column); - IntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref r0); - - return new RefEnumerable(array, offset, height, width); -#endif - } - - /// - /// Creates a new over an input 2D array. - /// - /// The type of elements in the input 2D array instance. - /// The input 2D array instance. - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span2D AsSpan2D(this T[,]? array) - { - return new(array); - } - - /// - /// Creates a new over an input 2D array. - /// - /// The type of elements in the input 2D array instance. - /// The input 2D array instance. - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for . - /// - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span2D AsSpan2D(this T[,]? array, int row, int column, int height, int width) - { - return new(array, row, column, height, width); - } - - /// - /// Creates a new over an input 2D array. - /// - /// The type of elements in the input 2D array instance. - /// The input 2D array instance. - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory2D AsMemory2D(this T[,]? array) - { - return new(array); - } - - /// - /// Creates a new over an input 2D array. - /// - /// The type of elements in the input 2D array instance. - /// The input 2D array instance. - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for . - /// - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory2D AsMemory2D(this T[,]? array, int row, int column, int height, int width) - { - return new(array, row, column, height, width); - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Returns a over a row in a given 2D array instance. - /// - /// The type of elements in the input 2D array instance. - /// The input array instance. - /// The target row to retrieve (0-based index). - /// A with the items from the target row within . - /// Thrown when doesn't match . - /// Thrown when is invalid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this T[,] array, int row) - { - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - if ((uint)row >= (uint)array.GetLength(0)) - { - ThrowArgumentOutOfRangeExceptionForRow(); - } - - ref T r0 = ref array.DangerousGetReferenceAt(row, 0); - - return MemoryMarshal.CreateSpan(ref r0, array.GetLength(1)); - } - - /// - /// Returns a over a row in a given 2D array instance. - /// - /// The type of elements in the input 2D array instance. - /// The input array instance. - /// The target row to retrieve (0-based index). - /// A with the items from the target row within . - /// Thrown when doesn't match . - /// Thrown when is invalid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory GetRowMemory(this T[,] array, int row) - { - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - if ((uint)row >= (uint)array.GetLength(0)) - { - ThrowArgumentOutOfRangeExceptionForRow(); - } - - ref T r0 = ref array.DangerousGetReferenceAt(row, 0); - IntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref r0); - - return new RawObjectMemoryManager(array, offset, array.GetLength(1)).Memory; - } - - /// - /// Creates a new over an input 2D array. - /// - /// The type of elements in the input 2D array instance. - /// The input 2D array instance. - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory AsMemory(this T[,]? array) - { - if (array is null) - { - return default; - } - - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - IntPtr offset = RuntimeHelpers.GetArray2DDataByteOffset(); - int length = array.Length; - - return new RawObjectMemoryManager(array, offset, length).Memory; - } - - /// - /// Creates a new over an input 2D array. - /// - /// The type of elements in the input 2D array instance. - /// The input 2D array instance. - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(this T[,]? array) - { - if (array is null) - { - return default; - } - - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - ref T r0 = ref array.DangerousGetReference(); - int length = array.Length; - - return MemoryMarshal.CreateSpan(ref r0, length); - } -#endif - - /// - /// Counts the number of occurrences of a given value into a target 2D array instance. - /// - /// The type of items in the input 2D array instance. - /// The input 2D array instance. - /// The value to look for. - /// The number of occurrences of in . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int Count(this T[,] array, T value) - where T : IEquatable - { - ref T r0 = ref array.DangerousGetReference(); - nint - length = RuntimeHelpers.GetArrayNativeLength(array), - count = SpanHelper.Count(ref r0, length, value); - - if ((nuint)count > int.MaxValue) - { - ThrowOverflowException(); - } - - return (int)count; - } - - /// - /// Gets a content hash from the input 2D array instance using the Djb2 algorithm. - /// For more info, see the documentation for . - /// - /// The type of items in the input 2D array instance. - /// The input 2D array instance. - /// The Djb2 value for the input 2D array instance. - /// The Djb2 hash is fully deterministic and with no random components. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetDjb2HashCode(this T[,] array) - where T : notnull - { - ref T r0 = ref array.DangerousGetReference(); - nint length = RuntimeHelpers.GetArrayNativeLength(array); - - return SpanHelper.GetDjb2HashCode(ref r0, length); - } - - /// - /// Checks whether or not a given array is covariant. - /// - /// The type of items in the input array instance. - /// The input array instance. - /// Whether or not is covariant. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsCovariant(this T[,] array) - { - return default(T) is null && array.GetType() != typeof(T[,]); - } - - /// - /// Throws an when using an array of an invalid type. - /// - private static void ThrowArrayTypeMismatchException() - { - throw new ArrayTypeMismatchException("The given array doesn't match the specified type T"); - } - - /// - /// Throws an when the "row" parameter is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForRow() - { - throw new ArgumentOutOfRangeException("row"); - } - - /// - /// Throws an when the "column" parameter is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForColumn() - { - throw new ArgumentOutOfRangeException("column"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs deleted file mode 100644 index c74f381efa2..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs +++ /dev/null @@ -1,324 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Helpers; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Internals; -#endif -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static partial class ArrayExtensions - { - /// - /// Returns a reference to the first element within a given 3D array, with no bounds checks. - /// - /// The type of elements in the input 3D array instance. - /// The input array instance. - /// A reference to the first element within , or the location it would have used, if is empty. - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReference(this T[,,] array) - { -#if NETCORE_RUNTIME - var arrayData = Unsafe.As(array)!; - ref T r0 = ref Unsafe.As(ref arrayData.Data); - - return ref r0; -#else - IntPtr offset = RuntimeHelpers.GetArray3DDataByteOffset(); - - return ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, offset); -#endif - } - - /// - /// Returns a reference to an element at a specified coordinate within a given 3D array, with no bounds checks. - /// - /// The type of elements in the input 3D array instance. - /// The input 2D array instance. - /// The depth index of the element to retrieve within . - /// The vertical index of the element to retrieve within . - /// The horizontal index of the element to retrieve within . - /// A reference to the element within at the coordinate specified by and . - /// - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the - /// and parameters are valid. Furthermore, this extension will ignore the lower bounds for the input - /// array, and will just assume that the input index is 0-based. It is responsibility of the caller to adjust the input - /// indices to account for the actual lower bounds, if the input array has either axis not starting at 0. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReferenceAt(this T[,,] array, int i, int j, int k) - { -#if NETCORE_RUNTIME - var arrayData = Unsafe.As(array)!; - nint offset = - ((nint)(uint)i * (nint)(uint)arrayData.Height * (nint)(uint)arrayData.Width) + - ((nint)(uint)j * (nint)(uint)arrayData.Width) + (nint)(uint)k; - ref T r0 = ref Unsafe.As(ref arrayData.Data); - ref T ri = ref Unsafe.Add(ref r0, offset); - - return ref ri; -#else - int - height = array.GetLength(1), - width = array.GetLength(2); - nint index = - ((nint)(uint)i * (nint)(uint)height * (nint)(uint)width) + - ((nint)(uint)j * (nint)(uint)width) + (nint)(uint)k; - IntPtr offset = RuntimeHelpers.GetArray3DDataByteOffset(); - ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, offset); - ref T ri = ref Unsafe.Add(ref r0, index); - - return ref ri; -#endif - } - -#if NETCORE_RUNTIME - // See description for this in the 2D partial file. - // Using the CHW naming scheme here (like with RGB images). - [StructLayout(LayoutKind.Sequential)] - private sealed class RawArray3DData - { -#pragma warning disable CS0649 // Unassigned fields -#pragma warning disable SA1401 // Fields should be private - public IntPtr Length; - public int Channel; - public int Height; - public int Width; - public int ChannelLowerBound; - public int HeightLowerBound; - public int WidthLowerBound; - public byte Data; -#pragma warning restore CS0649 -#pragma warning restore SA1401 - } -#endif - -#if SPAN_RUNTIME_SUPPORT - /// - /// Creates a new over an input 3D array. - /// - /// The type of elements in the input 3D array instance. - /// The input 3D array instance. - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory AsMemory(this T[,,]? array) - { - if (array is null) - { - return default; - } - - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - IntPtr offset = RuntimeHelpers.GetArray3DDataByteOffset(); - int length = array.Length; - - return new RawObjectMemoryManager(array, offset, length).Memory; - } - - /// - /// Creates a new over an input 3D array. - /// - /// The type of elements in the input 3D array instance. - /// The input 3D array instance. - /// A instance with the values of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(this T[,,]? array) - { - if (array is null) - { - return default; - } - - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - ref T r0 = ref array.DangerousGetReference(); - int length = array.Length; - - return MemoryMarshal.CreateSpan(ref r0, length); - } - - /// - /// Creates a new instance of the struct wrapping a layer in a 3D array. - /// - /// The type of elements in the input 3D array instance. - /// The given 3D array to wrap. - /// The target layer to map within . - /// Thrown when doesn't match . - /// Thrown when is invalid. - /// A instance wrapping the target layer within . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(this T[,,] array, int depth) - { - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowArgumentOutOfRangeExceptionForDepth(); - } - - ref T r0 = ref array.DangerousGetReferenceAt(depth, 0, 0); - int length = checked(array.GetLength(1) * array.GetLength(2)); - - return MemoryMarshal.CreateSpan(ref r0, length); - } - - /// - /// Creates a new instance of the struct wrapping a layer in a 3D array. - /// - /// The type of elements in the input 3D array instance. - /// The given 3D array to wrap. - /// The target layer to map within . - /// Thrown when doesn't match . - /// Thrown when is invalid. - /// A instance wrapping the target layer within . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory AsMemory(this T[,,] array, int depth) - { - if (array.IsCovariant()) - { - ThrowArrayTypeMismatchException(); - } - - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowArgumentOutOfRangeExceptionForDepth(); - } - - ref T r0 = ref array.DangerousGetReferenceAt(depth, 0, 0); - IntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref r0); - int length = checked(array.GetLength(1) * array.GetLength(2)); - - return new RawObjectMemoryManager(array, offset, length).Memory; - } -#endif - - /// - /// Creates a new instance of the struct wrapping a layer in a 3D array. - /// - /// The type of elements in the input 3D array instance. - /// The given 3D array to wrap. - /// The target layer to map within . - /// - /// Thrown when doesn't match . - /// - /// Thrown when either is invalid. - /// A instance wrapping the target layer within . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span2D AsSpan2D(this T[,,] array, int depth) - { - return new(array, depth); - } - - /// - /// Creates a new instance of the struct wrapping a layer in a 3D array. - /// - /// The type of elements in the input 3D array instance. - /// The given 3D array to wrap. - /// The target layer to map within . - /// - /// Thrown when doesn't match . - /// - /// Thrown when either is invalid. - /// A instance wrapping the target layer within . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory2D AsMemory2D(this T[,,] array, int depth) - { - return new(array, depth); - } - - /// - /// Counts the number of occurrences of a given value into a target 3D array instance. - /// - /// The type of items in the input 3D array instance. - /// The input 3D array instance. - /// The value to look for. - /// The number of occurrences of in . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Count(this T[,,] array, T value) - where T : IEquatable - { - ref T r0 = ref array.DangerousGetReference(); - nint - length = RuntimeHelpers.GetArrayNativeLength(array), - count = SpanHelper.Count(ref r0, length, value); - - if ((nuint)count > int.MaxValue) - { - ThrowOverflowException(); - } - - return (int)count; - } - - /// - /// Gets a content hash from the input 3D array instance using the Djb2 algorithm. - /// For more info, see the documentation for . - /// - /// The type of items in the input 3D array instance. - /// The input 3D array instance. - /// The Djb2 value for the input 3D array instance. - /// The Djb2 hash is fully deterministic and with no random components. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetDjb2HashCode(this T[,,] array) - where T : notnull - { - ref T r0 = ref array.DangerousGetReference(); - nint length = RuntimeHelpers.GetArrayNativeLength(array); - - return SpanHelper.GetDjb2HashCode(ref r0, length); - } - - /// - /// Checks whether or not a given array is covariant. - /// - /// The type of items in the input array instance. - /// The input array instance. - /// Whether or not is covariant. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsCovariant(this T[,,] array) - { - return default(T) is null && array.GetType() != typeof(T[,,]); - } - - /// - /// Throws an when the "depth" parameter is invalid. - /// - private static void ThrowArgumentOutOfRangeExceptionForDepth() - { - throw new ArgumentOutOfRangeException("depth"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolBufferWriterExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolBufferWriterExtensions.cs deleted file mode 100644 index fdb6f6b08a9..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolBufferWriterExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using System.IO; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Buffers; -using Microsoft.Toolkit.HighPerformance.Streams; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class ArrayPoolBufferWriterExtensions - { - /// - /// Returns a that can be used to write to a target an of instance. - /// - /// The target instance. - /// A wrapping and writing data to its underlying buffer. - /// The returned can only be written to and does not support seeking. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Stream AsStream(this ArrayPoolBufferWriter writer) - { - return new IBufferWriterStream(new ArrayBufferWriterOwner(writer)); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs deleted file mode 100644 index e169a880637..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class ArrayPoolExtensions - { - /// - /// Changes the number of elements of a rented one-dimensional array to the specified new size. - /// - /// The type of items into the target array to resize. - /// The target instance to use to resize the array. - /// The rented array to resize, or to create a new array. - /// The size of the new array. - /// Indicates whether the contents of the array should be cleared before reuse. - /// Thrown when is less than 0. - /// When this method returns, the caller must not use any references to the old array anymore. - public static void Resize(this ArrayPool pool, ref T[]? array, int newSize, bool clearArray = false) - { - // If the old array is null, just create a new one with the requested size - if (array is null) - { - array = pool.Rent(newSize); - - return; - } - - // If the new size is the same as the current size, do nothing - if (array.Length == newSize) - { - return; - } - - // Rent a new array with the specified size, and copy as many items from the current array - // as possible to the new array. This mirrors the behavior of the Array.Resize API from - // the BCL: if the new size is greater than the length of the current array, copy all the - // items from the original array into the new one. Otherwise, copy as many items as possible, - // until the new array is completely filled, and ignore the remaining items in the first array. - T[] newArray = pool.Rent(newSize); - int itemsToCopy = Math.Min(array.Length, newSize); - - Array.Copy(array, 0, newArray, 0, itemsToCopy); - - pool.Return(array, clearArray); - - array = newArray; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs deleted file mode 100644 index 62b827f1598..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/BoolExtensions.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class BoolExtensions - { - /// - /// Converts the given value into a . - /// - /// The input value to convert. - /// 1 if is , 0 otherwise. - /// This method does not contain branching instructions. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe byte ToByte(this bool flag) - { - // Whenever we need to take the address of an argument, we make a local copy first. - // This will be removed by the JIT anyway, but it can help produce better codegen and - // 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; - - return *(byte*)© - } - - /// - /// Converts the given value to an mask with - /// all bits representing the value of the input flag (either 0xFFFFFFFF or 0x00000000). - /// - /// The input value to convert. - /// 0xFFFFFFFF if is , 0x00000000 otherwise. - /// - /// This method does not contain branching instructions, and it is only guaranteed to work with - /// values being either 0 or 1. Operations producing a result, - /// such as numerical comparisons, always result in a valid value. If the value is - /// produced by fields with a custom , - /// or by using or other unsafe APIs to directly manipulate the underlying - /// data though, it is responsibility of the caller to ensure the validity of the provided value. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int ToBitwiseMask32(this bool flag) - { - bool copy = flag; - byte rangeFlag = *(byte*)© - int - negativeFlag = rangeFlag - 1, - mask = ~negativeFlag; - - return mask; - } - - /// - /// Converts the given value to a mask with - /// all bits representing the value of the input flag (either all 1s or 0s). - /// - /// The input value to convert. - /// All 1s if is , all 0s otherwise. - /// This method does not contain branching instructions. See additional note in . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe long ToBitwiseMask64(this bool flag) - { - bool copy = flag; - byte rangeFlag = *(byte*)© - long - negativeFlag = (long)rangeFlag - 1, - mask = ~negativeFlag; - - return mask; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/HashCodeExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/HashCodeExtensions.cs deleted file mode 100644 index cb8741062a9..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/HashCodeExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NETSTANDARD1_4 - -using System; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Helpers; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class HashCodeExtensions - { - /// - /// Adds a sequence of values to the hash code. - /// - /// The type of elements in the input instance. - /// The input instance. - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Add(ref this HashCode hashCode, ReadOnlySpan span) - where T : notnull - { - int hash = HashCode.CombineValues(span); - - hashCode.Add(hash); - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/IBufferWriterExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/IBufferWriterExtensions.cs deleted file mode 100644 index 8586be0bfee..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/IBufferWriterExtensions.cs +++ /dev/null @@ -1,136 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics.Contracts; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers; -using Microsoft.Toolkit.HighPerformance.Streams; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class IBufferWriterExtensions - { - /// - /// Returns a that can be used to write to a target an of instance. - /// - /// The target instance. - /// A wrapping and writing data to its underlying buffer. - /// The returned can only be written to and does not support seeking. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Stream AsStream(this IBufferWriter writer) - { - if (writer.GetType() == typeof(ArrayPoolBufferWriter)) - { - // If the input writer is of type ArrayPoolBufferWriter, we can use the type - // specific buffer writer owner to let the JIT elide callvirts when accessing it. - var internalWriter = Unsafe.As>(writer)!; - - return new IBufferWriterStream(new ArrayBufferWriterOwner(internalWriter)); - } - - return new IBufferWriterStream(new IBufferWriterOwner(writer)); - } - - /// - /// Writes a value of a specified type into a target instance. - /// - /// The type of value to write. - /// The target instance to write to. - /// The input value to write to . - /// Thrown if reaches the end. - public static void Write(this IBufferWriter writer, T value) - where T : unmanaged - { - int length = Unsafe.SizeOf(); - Span span = writer.GetSpan(1); - - if (span.Length < length) - { - ThrowArgumentExceptionForEndOfBuffer(); - } - - ref byte r0 = ref MemoryMarshal.GetReference(span); - - Unsafe.WriteUnaligned(ref r0, value); - - writer.Advance(length); - } - - /// - /// Writes a value of a specified type into a target instance. - /// - /// The type of value to write. - /// The target instance to write to. - /// The input value to write to . - /// Thrown if reaches the end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Write(this IBufferWriter writer, T value) - { - Span span = writer.GetSpan(1); - - if (span.Length < 1) - { - ThrowArgumentExceptionForEndOfBuffer(); - } - - MemoryMarshal.GetReference(span) = value; - - writer.Advance(1); - } - - /// - /// Writes a series of items of a specified type into a target instance. - /// - /// The type of value to write. - /// The target instance to write to. - /// The input to write to . - /// Thrown if reaches the end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Write(this IBufferWriter writer, ReadOnlySpan span) - where T : unmanaged - { - ReadOnlySpan source = MemoryMarshal.AsBytes(span); - Span destination = writer.GetSpan(source.Length); - - source.CopyTo(destination); - - writer.Advance(source.Length); - } - -#if !SPAN_RUNTIME_SUPPORT - /// - /// Writes a series of items of a specified type into a target instance. - /// - /// The type of value to write. - /// The target instance to write to. - /// The input to write to . - /// Thrown if reaches the end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Write(this IBufferWriter writer, ReadOnlySpan span) - { - Span destination = writer.GetSpan(span.Length); - - span.CopyTo(destination); - - writer.Advance(span.Length); - } -#endif - - /// - /// Throws an when trying to write too many bytes to the target writer. - /// - private static void ThrowArgumentExceptionForEndOfBuffer() - { - throw new ArgumentException("The current buffer writer can't contain the requested input data."); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/IMemoryOwnerExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/IMemoryOwnerExtensions.cs deleted file mode 100644 index f5e905d4753..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/IMemoryOwnerExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics.Contracts; -using System.IO; -using System.Runtime.CompilerServices; -using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class IMemoryOwnerExtensions - { - /// - /// Returns a wrapping the contents of the given of instance. - /// - /// The input of instance. - /// A wrapping the data within . - /// - /// The caller does not need to track the lifetime of the input of - /// instance, as the returned will take care of disposing that buffer when it is closed. - /// - /// Thrown when has an invalid data store. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Stream AsStream(this IMemoryOwner memoryOwner) - { - return MemoryStream.Create(memoryOwner); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ListExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ListExtensions.cs deleted file mode 100644 index 962e3f4a237..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ListExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if NET5_0 - -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class ListExtensions - { - /// - /// Creates a new over an input instance. - /// - /// The type of elements in the input instance. - /// The input instance. - /// A instance with the values of . - /// - /// Note that the returned is only guaranteed to be valid as long as the items within - /// are not modified. Doing so might cause the to swap its - /// internal buffer, causing the returned to become out of date. That means that in this - /// scenario, the would end up wrapping an array no longer in use. Always make sure to use - /// the returned while the target is not modified. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsSpan(this List? list) - { - return CollectionsMarshal.AsSpan(list); - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/MemoryExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/MemoryExtensions.cs deleted file mode 100644 index e107cdda6e9..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/MemoryExtensions.cs +++ /dev/null @@ -1,119 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class MemoryExtensions - { -#if SPAN_RUNTIME_SUPPORT - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory2D AsMemory2D(this Memory memory, int height, int width) - { - return new(memory, height, width); - } - - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory2D AsMemory2D(this Memory memory, int offset, int height, int width, int pitch) - { - return new(memory, offset, height, width, pitch); - } -#endif - - /// - /// Casts a of one primitive type to of bytes. - /// - /// The type if items in the source . - /// The source , of type . - /// A of bytes. - /// - /// Thrown if the property of the new would exceed . - /// - /// Thrown when the data store of is not supported. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory AsBytes(this Memory memory) - where T : unmanaged - { - return MemoryMarshal.AsMemory(((ReadOnlyMemory)memory).Cast()); - } - - /// - /// Casts a of one primitive type to another primitive type . - /// - /// The type of items in the source . - /// The type of items in the destination . - /// The source , of type . - /// A of type - /// Thrown when the data store of is not supported. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory Cast(this Memory memory) - where TFrom : unmanaged - where TTo : unmanaged - { - return MemoryMarshal.AsMemory(((ReadOnlyMemory)memory).Cast()); - } - - /// - /// Returns a wrapping the contents of the given of instance. - /// - /// The input of instance. - /// A wrapping the data within . - /// - /// Since this method only receives a instance, which does not track - /// the lifetime of its underlying buffer, it is responsibility of the caller to manage that. - /// In particular, the caller must ensure that the target buffer is not disposed as long - /// as the returned is in use, to avoid unexpected issues. - /// - /// Thrown when has an invalid data store. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Stream AsStream(this Memory memory) - { - return MemoryStream.Create(memory, false); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/NullableExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/NullableExtensions.cs deleted file mode 100644 index 43ea426f946..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/NullableExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// This extension is restricted to the .NET 5 because it shares the same BCL -// across all targets, ensuring that the layout of our Nullable mapping type -// will be correct. Exposing this API on older targets (especially .NET Standard) -// is not guaranteed to be correct and could result in invalid memory accesses. -#if NET5_0 - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Extensions -{ - /// - /// Helpers for working with the type. - /// - public static class NullableExtensions - { - /// - /// Returns a reference to the value of the input instance, regardless of whether - /// the property is returning or not. If that is not - /// the case, this method will still return a reference to the underlying value. - /// - /// The type of the underlying value - /// The - /// A reference to the underlying value from the input instance. - /// - /// Note that attempting to mutate the returned reference will not change the value returned by . - /// That means that reassigning the value of an empty instance will not make return . - /// - public static ref T DangerousGetValueOrDefaultReference(this ref T? value) - where T : struct - { - return ref Unsafe.As>(ref value).Value; - } - - /// - /// Mapping type that reflects the internal layout of the type. - /// See https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Nullable.cs. - /// - /// The value type wrapped by the current instance. - private struct RawNullableData - where T : struct - { -#pragma warning disable CS0649 // Unassigned fields - public bool HasValue; - public T Value; -#pragma warning restore CS0649 - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlyMemoryExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlyMemoryExtensions.cs deleted file mode 100644 index bbec0ff8e9b..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlyMemoryExtensions.cs +++ /dev/null @@ -1,158 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics.Contracts; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Buffers.Internals; -using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces; -using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class ReadOnlyMemoryExtensions - { -#if SPAN_RUNTIME_SUPPORT - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory2D AsMemory2D(this ReadOnlyMemory memory, int height, int width) - { - return new(memory, height, width); - } - - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory2D AsMemory2D(this ReadOnlyMemory memory, int offset, int height, int width, int pitch) - { - return new(memory, offset, height, width, pitch); - } -#endif - - /// - /// Casts a of one primitive type to of bytes. - /// - /// The type if items in the source . - /// The source , of type . - /// A of bytes. - /// - /// Thrown if the property of the new would exceed . - /// - /// Thrown when the data store of is not supported. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory AsBytes(this ReadOnlyMemory memory) - where T : unmanaged - { - return Cast(memory); - } - - /// - /// Casts a of one primitive type to another primitive type . - /// - /// The type of items in the source . - /// The type of items in the destination . - /// The source , of type . - /// A of type - /// Thrown when the data store of is not supported. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory Cast(this ReadOnlyMemory memory) - where TFrom : unmanaged - where TTo : unmanaged - { - if (memory.IsEmpty) - { - return default; - } - - if (typeof(TFrom) == typeof(char) && - MemoryMarshal.TryGetString((ReadOnlyMemory)(object)memory, out string? text, out int start, out int length)) - { - return new StringMemoryManager(text!, start, length).Memory; - } - - if (MemoryMarshal.TryGetArray(memory, out ArraySegment segment)) - { - return new ArrayMemoryManager(segment.Array!, segment.Offset, segment.Count).Memory; - } - - if (MemoryMarshal.TryGetMemoryManager>(memory, out var memoryManager, out start, out length)) - { - // If the memory manager is the one resulting from a previous cast, we can use it directly to retrieve - // a new manager for the target type that wraps the original data store, instead of creating one that - // wraps the current manager. This ensures that doing repeated casts always results in only up to one - // indirection level in the chain of memory managers needed to access the target data buffer to use. - if (memoryManager is IMemoryManager wrappingManager) - { - return wrappingManager.GetMemory(start, length); - } - - return new ProxyMemoryManager(memoryManager, start, length).Memory; - } - - // Throws when the memory instance has an unsupported backing store - static ReadOnlyMemory ThrowArgumentExceptionForUnsupportedMemory() - { - throw new ArgumentException("The input instance doesn't have a supported underlying data store."); - } - - return ThrowArgumentExceptionForUnsupportedMemory(); - } - - /// - /// Returns a wrapping the contents of the given of instance. - /// - /// The input of instance. - /// A wrapping the data within . - /// - /// Since this method only receives a instance, which does not track - /// the lifetime of its underlying buffer, it is responsibility of the caller to manage that. - /// In particular, the caller must ensure that the target buffer is not disposed as long - /// as the returned is in use, to avoid unexpected issues. - /// - /// Thrown when has an invalid data store. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Stream AsStream(this ReadOnlyMemory memory) - { - return MemoryStream.Create(memory, true); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs deleted file mode 100644 index 6dd5263dccd..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs +++ /dev/null @@ -1,403 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Enumerables; -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class ReadOnlySpanExtensions - { - /// - /// Returns a reference to the first element within a given , with no bounds checks. - /// - /// The type of elements in the input instance. - /// The input instance. - /// A reference to the first element within . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReference(this ReadOnlySpan span) - { - return ref MemoryMarshal.GetReference(span); - } - - /// - /// Returns a reference to an element at a specified index within a given , with no bounds checks. - /// - /// The type of elements in the input instance. - /// The input instance. - /// The index of the element to retrieve within . - /// A reference to the element within at the index specified by . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReferenceAt(this ReadOnlySpan span, int i) - { - // Here we assume the input index will never be negative, so we do a (nint)(uint) cast - // to force the JIT to skip the sign extension when going from int to native int. - // On .NET Core 3.1, if we only use Unsafe.Add(ref r0, i), we get the following: - // ============================= - // L0000: mov rax, [rcx] - // L0003: movsxd rdx, edx - // L0006: lea rax, [rax+rdx*4] - // L000a: ret - // ============================= - // Note the movsxd (move with sign extension) to expand the index passed in edx to - // the whole rdx register. This is unnecessary and more expensive than just a mov, - // which when done to a large register size automatically zeroes the upper bits. - // With the (nint)(uint) cast, we get the following codegen instead: - // ============================= - // L0000: mov rax, [rcx] - // L0003: mov edx, edx - // L0005: lea rax, [rax+rdx*4] - // L0009: ret - // ============================= - // Here we can see how the index is extended to a native integer with just a mov, - // which effectively only zeroes the upper bits of the same register used as source. - // These three casts are a bit verbose, but they do the trick on both 32 bit and 64 - // bit architectures, producing optimal code in both cases (they are either completely - // elided on 32 bit systems, or result in the correct register expansion when on 64 bit). - // We first do an unchecked conversion to uint (which is just a reinterpret-cast). We - // then cast to nint, so that we can obtain an IntPtr value without the range check (since - // uint could be out of range there if the original index was negative). The final result - // is a clean mov as shown above. This will eventually be natively supported by the JIT - // compiler (see https://github.com/dotnet/runtime/issues/38794), but doing this here - // still ensures the optimal codegen even on existing runtimes (eg. .NET Core 2.1 and 3.1). - ref T r0 = ref MemoryMarshal.GetReference(span); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i); - - return ref ri; - } - - /// - /// Returns a reference to an element at a specified index within a given , with no bounds checks. - /// - /// The type of elements in the input instance. - /// The input instance. - /// The index of the element to retrieve within . - /// A reference to the element within at the index specified by . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReferenceAt(this ReadOnlySpan span, nint i) - { - ref T r0 = ref MemoryMarshal.GetReference(span); - ref T ri = ref Unsafe.Add(ref r0, i); - - return ref ri; - } - - /// - /// Returns a reference to the first element within a given , clamping the input index in the valid range. - /// If the parameter exceeds the length of , it will be clamped to 0. - /// Therefore, the returned reference will always point to a valid element within , assuming it is not empty. - /// This method is specifically meant to efficiently index lookup tables, especially if they point to constant data. - /// Consider this example where a lookup table is used to validate whether a given character is within a specific set: - /// - /// public static ReadOnlySpan<bool> ValidSetLookupTable => new bool[] - /// { - /// false, true, true, true, true, true, false, true, - /// false, false, true, false, true, false, true, false, - /// true, false, false, true, false, false, false, false, - /// false, false, false, false, true, true, false, true - /// }; - /// - /// int ch = Console.Read(); - /// bool isValid = ValidSetLookupTable.DangerousGetLookupReference(ch); - /// - /// Even if the input index is outside the range of the lookup table, being clamped to 0, it will - /// just cause the value 0 to be returned in this case, which is functionally the same for the check - /// being performed. This extension can easily be used whenever the first position in a lookup - /// table being referenced corresponds to a falsey value, like in this case. - /// Additionally, the example above leverages a compiler optimization introduced with C# 7.3, - /// which allows instances pointing to compile-time constant data - /// to be directly mapped to the static .text section in the final assembly: the array being - /// created in code will never actually be allocated, and the will - /// just point to constant data. Note that this only works for blittable values that are not - /// dependent on the byte endianness of the system, like or . - /// For more info, see . - /// - /// The type of elements in the input instance. - /// The input instance. - /// The index of the element to retrieve within . - /// - /// A reference to the element within at the index specified by , - /// or a reference to the first element within if was not a valid index. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ref readonly T DangerousGetLookupReferenceAt(this ReadOnlySpan span, int i) - { - // Check whether the input is in range by first casting both - // operands to uint and then comparing them, as this allows - // the test to also identify cases where the input index is - // less than zero. The resulting bool is then reinterpreted - // as a byte (either 1 or 0), and then decremented. - // This will result in either 0 if the input index was - // valid for the target span, or -1 (0xFFFFFFFF) otherwise. - // The result is then negated, producing the value 0xFFFFFFFF - // for valid indices, or 0 otherwise. The generated mask - // is then combined with the original index. This leaves - // the index intact if it was valid, otherwise zeros it. - // The computed offset is finally used to access the - // lookup table, and it is guaranteed to never go out of - // bounds unless the input span was just empty, which for a - // lookup table can just be assumed to always be false. - bool isInRange = (uint)i < (uint)span.Length; - byte rangeFlag = *(byte*)&isInRange; - uint - negativeFlag = unchecked(rangeFlag - 1u), - mask = ~negativeFlag, - offset = (uint)i & mask; - ref T r0 = ref MemoryMarshal.GetReference(span); - ref T r1 = ref Unsafe.Add(ref r0, (nint)offset); - - return ref r1; - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan2D AsSpan2D(this ReadOnlySpan span, int height, int width) - { - return new(span, height, width); - } - - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan2D AsSpan2D(this ReadOnlySpan span, int offset, int height, int width, int pitch) - { - return new(span, offset, height, width, pitch); - } -#endif - - /// - /// Gets the index of an element of a given from its reference. - /// - /// The type if items in the input . - /// The input to calculate the index for. - /// The reference to the target item to get the index for. - /// The index of within . - /// Thrown if does not belong to . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this ReadOnlySpan span, in T value) - { - ref T r0 = ref MemoryMarshal.GetReference(span); - ref T r1 = ref Unsafe.AsRef(value); - IntPtr byteOffset = Unsafe.ByteOffset(ref r0, ref r1); - - nint elementOffset = byteOffset / (nint)(uint)Unsafe.SizeOf(); - - if ((nuint)elementOffset >= (uint)span.Length) - { - SpanExtensions.ThrowArgumentOutOfRangeExceptionForInvalidReference(); - } - - return (int)elementOffset; - } - - /// - /// Counts the number of occurrences of a given value into a target instance. - /// - /// The type of items in the input instance. - /// The input instance to read. - /// The value to look for. - /// The number of occurrences of in . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Count(this ReadOnlySpan span, T value) - where T : IEquatable - { - ref T r0 = ref MemoryMarshal.GetReference(span); - nint length = (nint)(uint)span.Length; - - return (int)SpanHelper.Count(ref r0, length, value); - } - - /// - /// Casts a of one primitive type to of bytes. - /// - /// The type if items in the source . - /// The source slice, of type . - /// A of bytes. - /// - /// Thrown if the property of the new would exceed . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsBytes(this ReadOnlySpan span) - where T : unmanaged - { - return MemoryMarshal.AsBytes(span); - } - - /// - /// Casts a of one primitive type to another primitive type . - /// - /// The type of items in the source . - /// The type of items in the destination . - /// The source slice, of type . - /// A of type - /// - /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan Cast(this ReadOnlySpan span) - where TFrom : unmanaged - where TTo : unmanaged - { - return MemoryMarshal.Cast(span); - } - - /// - /// Enumerates the items in the input instance, as pairs of value/index values. - /// This extension should be used directly within a loop: - /// - /// ReadOnlySpan<string> words = new[] { "Hello", ", ", "world", "!" }; - /// - /// foreach (var item in words.Enumerate()) - /// { - /// // Access the index and value of each item here... - /// int index = item.Index; - /// string value = item.Value; - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The type of items to enumerate. - /// The source to enumerate. - /// A wrapper type that will handle the value/index enumeration for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpanEnumerable Enumerate(this ReadOnlySpan span) - { - return new(span); - } - - /// - /// Tokenizes the values in the input instance using a specified separator. - /// This extension should be used directly within a loop: - /// - /// ReadOnlySpan<char> text = "Hello, world!"; - /// - /// foreach (var token in text.Tokenize(',')) - /// { - /// // Access the tokens here... - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The type of items in the to tokenize. - /// The source to tokenize. - /// The separator item to use. - /// A wrapper type that will handle the tokenization for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpanTokenizer Tokenize(this ReadOnlySpan span, T separator) - where T : IEquatable - { - return new(span, separator); - } - - /// - /// Gets a content hash from the input instance using the Djb2 algorithm. - /// It was designed by Daniel J. Bernstein and is a - /// non-cryptographic has function. - /// The main advantages of this algorithm are a good distribution of the resulting hash codes, which results in a relatively low - /// number of collisions, while at the same time being particularly fast to process, making it suitable for quickly hashing - /// even long sequences of values. For the reference implementation, see: . - /// For details on the used constants, see the details provided in this StackOverflow answer (as well as the accepted one): - /// . - /// Additionally, a comparison between some common hashing algorithms can be found in the reply to this StackExchange question: - /// . - /// Note that the exact implementation is slightly different in this method when it is not called on a sequence of - /// values: in this case the method will be invoked for each value in - /// the provided instance, and then those values will be combined using the Djb2 algorithm. - /// - /// The type of items in the input instance. - /// The input instance. - /// The Djb2 value for the input instance. - /// The Djb2 hash is fully deterministic and with no random components. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetDjb2HashCode(this ReadOnlySpan span) - where T : notnull - { - ref T r0 = ref MemoryMarshal.GetReference(span); - nint length = (nint)(uint)span.Length; - - return SpanHelper.GetDjb2HashCode(ref r0, length); - } - - /// - /// Copies the contents of a given into destination instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The instance to copy items into. - /// - /// Thrown when the destination is shorter than the source . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyTo(this ReadOnlySpan span, RefEnumerable destination) - { - destination.CopyFrom(span); - } - - /// - /// Attempts to copy the contents of a given into destination instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The instance to copy items into. - /// Whether or not the operation was successful. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryCopyTo(this ReadOnlySpan span, RefEnumerable destination) - { - return destination.TryCopyFrom(span); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/SpanExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/SpanExtensions.cs deleted file mode 100644 index 1bb8cd35fba..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/SpanExtensions.cs +++ /dev/null @@ -1,301 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Enumerables; -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class SpanExtensions - { - /// - /// Returns a reference to the first element within a given , with no bounds checks. - /// - /// The type of elements in the input instance. - /// The input instance. - /// A reference to the first element within . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReference(this Span span) - { - return ref MemoryMarshal.GetReference(span); - } - - /// - /// Returns a reference to an element at a specified index within a given , with no bounds checks. - /// - /// The type of elements in the input instance. - /// The input instance. - /// The index of the element to retrieve within . - /// A reference to the element within at the index specified by . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReferenceAt(this Span span, int i) - { - ref T r0 = ref MemoryMarshal.GetReference(span); - ref T ri = ref Unsafe.Add(ref r0, (nint)(uint)i); - - return ref ri; - } - - /// - /// Returns a reference to an element at a specified index within a given , with no bounds checks. - /// - /// The type of elements in the input instance. - /// The input instance. - /// The index of the element to retrieve within . - /// A reference to the element within at the index specified by . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetReferenceAt(this Span span, nint i) - { - ref T r0 = ref MemoryMarshal.GetReference(span); - ref T ri = ref Unsafe.Add(ref r0, i); - - return ref ri; - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span2D AsSpan2D(this Span span, int height, int width) - { - return new(span, height, width); - } - - /// - /// Returns a instance wrapping the underlying data for the given instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// The resulting instance. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span2D AsSpan2D(this Span span, int offset, int height, int width, int pitch) - { - return new(span, offset, height, width, pitch); - } -#endif - - /// - /// Casts a of one primitive type to of bytes. - /// - /// The type if items in the source . - /// The source slice, of type . - /// A of bytes. - /// - /// Thrown if the property of the new would exceed . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span AsBytes(this Span span) - where T : unmanaged - { - return MemoryMarshal.AsBytes(span); - } - - /// - /// Casts a of one primitive type to another primitive type . - /// - /// The type of items in the source . - /// The type of items in the destination . - /// The source slice, of type . - /// A of type - /// - /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span Cast(this Span span) - where TFrom : unmanaged - where TTo : unmanaged - { - return MemoryMarshal.Cast(span); - } - - /// - /// Gets the index of an element of a given from its reference. - /// - /// The type if items in the input . - /// The input to calculate the index for. - /// The reference to the target item to get the index for. - /// The index of within . - /// Thrown if does not belong to . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this Span span, ref T value) - { - ref T r0 = ref MemoryMarshal.GetReference(span); - IntPtr byteOffset = Unsafe.ByteOffset(ref r0, ref value); - - nint elementOffset = byteOffset / (nint)(uint)Unsafe.SizeOf(); - - if ((nuint)elementOffset >= (uint)span.Length) - { - ThrowArgumentOutOfRangeExceptionForInvalidReference(); - } - - return (int)elementOffset; - } - - /// - /// Counts the number of occurrences of a given value into a target instance. - /// - /// The type of items in the input instance. - /// The input instance to read. - /// The value to look for. - /// The number of occurrences of in . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Count(this Span span, T value) - where T : IEquatable - { - ref T r0 = ref MemoryMarshal.GetReference(span); - nint length = (nint)(uint)span.Length; - - return (int)SpanHelper.Count(ref r0, length, value); - } - - /// - /// Enumerates the items in the input instance, as pairs of reference/index values. - /// This extension should be used directly within a loop: - /// - /// Span<int> numbers = new[] { 1, 2, 3, 4, 5, 6, 7 }; - /// - /// foreach (var item in numbers.Enumerate()) - /// { - /// // Access the index and value of each item here... - /// int index = item.Index; - /// ref int value = ref item.Value; - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The type of items to enumerate. - /// The source to enumerate. - /// A wrapper type that will handle the reference/index enumeration for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanEnumerable Enumerate(this Span span) - { - return new(span); - } - - /// - /// Tokenizes the values in the input instance using a specified separator. - /// This extension should be used directly within a loop: - /// - /// Span<char> text = "Hello, world!".ToCharArray(); - /// - /// foreach (var token in text.Tokenize(',')) - /// { - /// // Access the tokens here... - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The type of items in the to tokenize. - /// The source to tokenize. - /// The separator item to use. - /// A wrapper type that will handle the tokenization for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SpanTokenizer Tokenize(this Span span, T separator) - where T : IEquatable - { - return new(span, separator); - } - - /// - /// Gets a content hash from the input instance using the Djb2 algorithm. - /// For more info, see the documentation for . - /// - /// The type of items in the input instance. - /// The input instance. - /// The Djb2 value for the input instance. - /// The Djb2 hash is fully deterministic and with no random components. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetDjb2HashCode(this Span span) - where T : notnull - { - ref T r0 = ref MemoryMarshal.GetReference(span); - nint length = (nint)(uint)span.Length; - - return SpanHelper.GetDjb2HashCode(ref r0, length); - } - - /// - /// Copies the contents of a given into destination instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The instance to copy items into. - /// - /// Thrown when the destination is shorter than the source . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CopyTo(this Span span, RefEnumerable destination) - { - destination.CopyFrom(span); - } - - /// - /// Attempts to copy the contents of a given into destination instance. - /// - /// The type of items in the input instance. - /// The input instance. - /// The instance to copy items into. - /// Whether or not the operation was successful. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryCopyTo(this Span span, RefEnumerable destination) - { - return destination.TryCopyFrom(span); - } - - /// - /// Throws an when the given reference is out of range. - /// - internal static void ThrowArgumentOutOfRangeExceptionForInvalidReference() - { - throw new ArgumentOutOfRangeException("value", "The input reference does not belong to an element of the input span"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/SpinLockExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/SpinLockExtensions.cs deleted file mode 100644 index 506fab69f24..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/SpinLockExtensions.cs +++ /dev/null @@ -1,188 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Threading; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class SpinLockExtensions - { - /// - /// Enters a specified instance and returns a wrapper to use to release the lock. - /// This extension should be used though a block or statement: - /// - /// SpinLock spinLock = new SpinLock(); - /// - /// using (SpinLockExtensions.Enter(&spinLock)) - /// { - /// // Thread-safe code here... - /// } - /// - /// The compiler will take care of releasing the SpinLock when the code goes out of that scope. - /// - /// A pointer to the target to use - /// A wrapper type that will release when its method is called. - /// The returned value shouldn't be used directly: use this extension in a block or statement. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe UnsafeLock Enter(SpinLock* spinLock) - { - return new(spinLock); - } - - /// - /// A that is used to enter and hold a through a block or statement. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public readonly unsafe ref struct UnsafeLock - { - /// - /// The * pointer to the target value to use. - /// - private readonly SpinLock* spinLock; - - /// - /// A value indicating whether or not the lock is taken by this instance. - /// - private readonly bool lockTaken; - - /// - /// Initializes a new instance of the struct. - /// - /// The target to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public UnsafeLock(SpinLock* spinLock) - { - this.spinLock = spinLock; - this.lockTaken = false; - - spinLock->Enter(ref this.lockTaken); - } - - /// - /// Implements the duck-typed method and releases the current instance. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - if (this.lockTaken) - { - this.spinLock->Exit(); - } - } - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Enters a specified instance and returns a wrapper to use to release the lock. - /// This extension should be used though a block or statement: - /// - /// SpinLock spinLock = new SpinLock(); - /// - /// using (spinLock.Enter()) - /// { - /// // Thread-safe code here... - /// } - /// - /// The compiler will take care of releasing the SpinLock when the code goes out of that scope. - /// - /// The target to use - /// A wrapper type that will release when its method is called. - /// The returned value shouldn't be used directly: use this extension in a block or statement. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Lock Enter(ref this SpinLock spinLock) - { - return new(ref spinLock); - } -#else - /// - /// Enters a specified instance and returns a wrapper to use to release the lock. - /// This extension should be used though a block or statement: - /// - /// private SpinLock spinLock = new SpinLock(); - /// - /// public void Foo() - /// { - /// using (SpinLockExtensions.Enter(this, ref spinLock)) - /// { - /// // Thread-safe code here... - /// } - /// } - /// - /// The compiler will take care of releasing the SpinLock when the code goes out of that scope. - /// - /// The owner to create a portable reference for. - /// The target to use (it must be within ). - /// A wrapper type that will release when its method is called. - /// The returned value shouldn't be used directly: use this extension in a block or statement. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Lock Enter(object owner, ref SpinLock spinLock) - { - return new(owner, ref spinLock); - } -#endif - - /// - /// A that is used to enter and hold a through a block or statement. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public readonly ref struct Lock - { - /// - /// The instance pointing to the target value to use. - /// - private readonly Ref spinLock; - - /// - /// A value indicating whether or not the lock is taken by this instance. - /// - private readonly bool lockTaken; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The target to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Lock(ref SpinLock spinLock) - { - this.spinLock = new Ref(ref spinLock); - this.lockTaken = false; - - spinLock.Enter(ref this.lockTaken); - } -#else - /// - /// Initializes a new instance of the struct. - /// - /// The owner to create a portable reference for. - /// The target to use (it must be within ). - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Lock(object owner, ref SpinLock spinLock) - { - this.spinLock = new Ref(owner, ref spinLock); - this.lockTaken = false; - - spinLock.Enter(ref this.lockTaken); - } -#endif - - /// - /// Implements the duck-typed method and releases the current instance. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - if (this.lockTaken) - { - this.spinLock.Value.Exit(); - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/StreamExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/StreamExtensions.cs deleted file mode 100644 index 22b91d083b7..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/StreamExtensions.cs +++ /dev/null @@ -1,253 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -#if !SPAN_RUNTIME_SUPPORT -using System.Buffers; -using System.Threading; -using System.Threading.Tasks; -#endif - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class StreamExtensions - { -#if !SPAN_RUNTIME_SUPPORT - /// - /// Asynchronously reads a sequence of bytes from a given instance. - /// - /// The source to read data from. - /// The destination to write data to. - /// The optional for the operation. - /// A representing the operation being performed. - public static ValueTask ReadAsync(this Stream stream, Memory buffer, CancellationToken cancellationToken = default) - { - if (cancellationToken.IsCancellationRequested) - { - return new ValueTask(Task.FromCanceled(cancellationToken)); - } - - // If the memory wraps an array, extract it and use it directly - if (MemoryMarshal.TryGetArray(buffer, out ArraySegment segment)) - { - return new ValueTask(stream.ReadAsync(segment.Array!, segment.Offset, segment.Count, cancellationToken)); - } - - // Local function used as the fallback path. This happens when the input memory - // doesn't wrap an array instance we can use. We use a local function as we need - // the body to be asynchronous, in order to execute the finally block after the - // write operation has been completed. By separating the logic, we can keep the - // main method as a synchronous, value-task returning function. This fallback - // path should hopefully be pretty rare, as memory instances are typically just - // created around arrays, often being rented from a memory pool in particular. - static async Task ReadAsyncFallback(Stream stream, Memory buffer, CancellationToken cancellationToken) - { - byte[] rent = ArrayPool.Shared.Rent(buffer.Length); - - try - { - int bytesRead = await stream.ReadAsync(rent, 0, buffer.Length, cancellationToken); - - if (bytesRead > 0) - { - rent.AsSpan(0, bytesRead).CopyTo(buffer.Span); - } - - return bytesRead; - } - finally - { - ArrayPool.Shared.Return(rent); - } - } - - return new ValueTask(ReadAsyncFallback(stream, buffer, cancellationToken)); - } - - /// - /// Asynchronously writes a sequence of bytes to a given instance. - /// - /// The destination to write data to. - /// The source to read data from. - /// The optional for the operation. - /// A representing the operation being performed. - public static ValueTask WriteAsync(this Stream stream, ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - if (cancellationToken.IsCancellationRequested) - { - return new ValueTask(Task.FromCanceled(cancellationToken)); - } - - if (MemoryMarshal.TryGetArray(buffer, out ArraySegment segment)) - { - return new ValueTask(stream.WriteAsync(segment.Array!, segment.Offset, segment.Count, cancellationToken)); - } - - // Local function, same idea as above - static async Task WriteAsyncFallback(Stream stream, ReadOnlyMemory buffer, CancellationToken cancellationToken) - { - byte[] rent = ArrayPool.Shared.Rent(buffer.Length); - - try - { - buffer.Span.CopyTo(rent); - - await stream.WriteAsync(rent, 0, buffer.Length, cancellationToken); - } - finally - { - ArrayPool.Shared.Return(rent); - } - } - - return new ValueTask(WriteAsyncFallback(stream, buffer, cancellationToken)); - } - - /// - /// Reads a sequence of bytes from a given instance. - /// - /// The source to read data from. - /// The target to write data to. - /// The number of bytes that have been read. - public static int Read(this Stream stream, Span buffer) - { - byte[] rent = ArrayPool.Shared.Rent(buffer.Length); - - try - { - int bytesRead = stream.Read(rent, 0, buffer.Length); - - if (bytesRead > 0) - { - rent.AsSpan(0, bytesRead).CopyTo(buffer); - } - - return bytesRead; - } - finally - { - ArrayPool.Shared.Return(rent); - } - } - - /// - /// Writes a sequence of bytes to a given instance. - /// - /// The destination to write data to. - /// The source to read data from. - public static void Write(this Stream stream, ReadOnlySpan buffer) - { - byte[] rent = ArrayPool.Shared.Rent(buffer.Length); - - try - { - buffer.CopyTo(rent); - - stream.Write(rent, 0, buffer.Length); - } - finally - { - ArrayPool.Shared.Return(rent); - } - } -#endif - - /// - /// Reads a value of a specified type from a source instance. - /// - /// The type of value to read. - /// The source instance to read from. - /// The value read from . - /// Thrown if reaches the end. -#if SPAN_RUNTIME_SUPPORT - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static T Read(this Stream stream) - where T : unmanaged - { -#if SPAN_RUNTIME_SUPPORT - T result = default; - int length = Unsafe.SizeOf(); - - unsafe - { - if (stream.Read(new Span(&result, length)) != length) - { - ThrowInvalidOperationExceptionForEndOfStream(); - } - } - - return result; -#else - int length = Unsafe.SizeOf(); - byte[] buffer = ArrayPool.Shared.Rent(length); - - try - { - if (stream.Read(buffer, 0, length) != length) - { - ThrowInvalidOperationExceptionForEndOfStream(); - } - - return Unsafe.ReadUnaligned(ref buffer[0]); - } - finally - { - ArrayPool.Shared.Return(buffer); - } -#endif - } - - /// - /// Writes a value of a specified type into a target instance. - /// - /// The type of value to write. - /// The target instance to write to. - /// The input value to write to . -#if SPAN_RUNTIME_SUPPORT - [MethodImpl(MethodImplOptions.AggressiveInlining)] -#endif - public static void Write(this Stream stream, in T value) - where T : unmanaged - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref Unsafe.AsRef(value); - ref byte r1 = ref Unsafe.As(ref r0); - int length = Unsafe.SizeOf(); - - ReadOnlySpan span = MemoryMarshal.CreateReadOnlySpan(ref r1, length); - - stream.Write(span); -#else - int length = Unsafe.SizeOf(); - byte[] buffer = ArrayPool.Shared.Rent(length); - - try - { - Unsafe.WriteUnaligned(ref buffer[0], value); - - stream.Write(buffer, 0, length); - } - finally - { - ArrayPool.Shared.Return(buffer); - } -#endif - } - - /// - /// Throws an when fails. - /// - private static void ThrowInvalidOperationExceptionForEndOfStream() - { - throw new InvalidOperationException("The stream didn't contain enough data to read the requested item"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Extensions/StringExtensions.cs b/Microsoft.Toolkit.HighPerformance/Extensions/StringExtensions.cs deleted file mode 100644 index c563bdd3e44..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Extensions/StringExtensions.cs +++ /dev/null @@ -1,168 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if NETCOREAPP2_1 || NETSTANDARD -using System.Runtime.InteropServices; -#endif -using Microsoft.Toolkit.HighPerformance.Enumerables; -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// Helpers for working with the type. - /// - public static class StringExtensions - { - /// - /// Returns a reference to the first element within a given , with no bounds checks. - /// - /// The input instance. - /// A reference to the first element within , or the location it would have used, if is empty. - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref char DangerousGetReference(this string text) - { -#if NETCOREAPP3_1 || NET5_0 - return ref Unsafe.AsRef(text.GetPinnableReference()); -#elif NETCOREAPP2_1 - var stringData = Unsafe.As(text)!; - - return ref stringData.Data; -#else - return ref MemoryMarshal.GetReference(text.AsSpan()); -#endif - } - - /// - /// Returns a reference to an element at a specified index within a given , with no bounds checks. - /// - /// The input instance. - /// The index of the element to retrieve within . - /// A reference to the element within at the index specified by . - /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref char DangerousGetReferenceAt(this string text, int i) - { -#if NETCOREAPP3_1 || NET5_0 - ref char r0 = ref Unsafe.AsRef(text.GetPinnableReference()); -#elif NETCOREAPP2_1 - ref char r0 = ref Unsafe.As(text)!.Data; -#else - ref char r0 = ref MemoryMarshal.GetReference(text.AsSpan()); -#endif - ref char ri = ref Unsafe.Add(ref r0, (nint)(uint)i); - - return ref ri; - } - -#if NETCOREAPP2_1 - // Description adapted from CoreCLR: see https://source.dot.net/#System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs,285. - // CLR strings are laid out in memory as follows: - // [ sync block || pMethodTable || length || string data .. ] - // ^ ^ - // | \-- ref Unsafe.As(text).Data - // \-- string - // The reference to RawStringData.Data points to the first character in the - // string, skipping over the sync block, method table and string length. - [StructLayout(LayoutKind.Explicit)] - private sealed class RawStringData - { -#pragma warning disable CS0649 // Unassigned fields -#pragma warning disable SA1401 // Fields should be private - [FieldOffset(4)] - public char Data; -#pragma warning restore CS0649 -#pragma warning restore SA1401 - } -#endif - - /// - /// Counts the number of occurrences of a given character into a target instance. - /// - /// The input instance to read. - /// The character to look for. - /// The number of occurrences of in . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Count(this string text, char c) - { - ref char r0 = ref text.DangerousGetReference(); - nint length = (nint)(uint)text.Length; - - return (int)SpanHelper.Count(ref r0, length, c); - } - - /// - /// Enumerates the items in the input instance, as pairs of value/index values. - /// This extension should be used directly within a loop: - /// - /// string text = "Hello, world!"; - /// - /// foreach (var item in text.Enumerate()) - /// { - /// // Access the index and value of each item here... - /// int index = item.Index; - /// char value = item.Value; - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The source to enumerate. - /// A wrapper type that will handle the value/index enumeration for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpanEnumerable Enumerate(this string text) - { - return new(text.AsSpan()); - } - - /// - /// Tokenizes the values in the input instance using a specified separator. - /// This extension should be used directly within a loop: - /// - /// string text = "Hello, world!"; - /// - /// foreach (var token in text.Tokenize(',')) - /// { - /// // Access the tokens here... - /// } - /// - /// The compiler will take care of properly setting up the loop with the type returned from this method. - /// - /// The source to tokenize. - /// The separator character to use. - /// A wrapper type that will handle the tokenization for . - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpanTokenizer Tokenize(this string text, char separator) - { - return new(text.AsSpan(), separator); - } - - /// - /// Gets a content hash from the input instance using the Djb2 algorithm. - /// For more info, see the documentation for . - /// - /// The source to enumerate. - /// The Djb2 value for the input instance. - /// The Djb2 hash is fully deterministic and with no random components. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetDjb2HashCode(this string text) - { - ref char r0 = ref text.DangerousGetReference(); - nint length = (nint)(uint)text.Length; - - return SpanHelper.GetDjb2HashCode(ref r0, length); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs b/Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs deleted file mode 100644 index 76916c26b91..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/BitHelper.cs +++ /dev/null @@ -1,467 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if NETCOREAPP3_1 || NET5_0 -using System.Runtime.Intrinsics.X86; -#endif - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to perform bit operations on numeric types. - /// - public static class BitHelper - { - /// - /// Checks whether or not a given bit is set. - /// - /// The input value. - /// The position of the bit to check (in [0, 31] range). - /// Whether or not the n-th bit is set. - /// - /// This method doesn't validate against the valid range. - /// If the parameter is not valid, the result will just be inconsistent. - /// Additionally, no conditional branches are used to retrieve the flag. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool HasFlag(uint value, int n) - { - // Read the n-th bit, downcast to byte - byte flag = (byte)((value >> n) & 1); - - // Reinterpret the byte to avoid the test, setnz and - // movzx instructions (asm x64). This is because the JIT - // compiler is able to optimize this reinterpret-cast as - // a single "and eax, 0x1" instruction, whereas if we had - // compared the previous computed flag against 0, the assembly - // would have had to perform the test, set the non-zero - // flag and then extend the (byte) result to eax. - return *(bool*)&flag; - } - - /// - /// Checks whether or not a given bit is set in a given bitwise lookup table. - /// This method provides a branchless, register-based (with no memory accesses) way to - /// check whether a given value is valid, according to a precomputed lookup table. - /// It is similar in behavior to , with the main difference - /// being that this method will also validate the input parameter, and - /// will always return if it falls outside of the expected interval. - /// Additionally, this method accepts a parameter, which is used to - /// decrement the input parameter to ensure that the range of accepted - /// values fits within the available 32 bits of the lookup table in use. - /// For more info on this optimization technique, see . - /// Here is how the code from the link above would be implemented using this method: - /// - /// bool IsReservedCharacter(char c) - /// { - /// return BitHelper.HasLookupFlag(314575237u, c, 36); - /// } - /// - /// The resulted assembly is virtually identical, with the added optimization that the one - /// produced by has no conditional branches at all. - /// - /// The input lookup table to use. - /// The input value to check. - /// The minimum accepted value for (defaults to 0). - /// Whether or not the corresponding flag for is set in . - /// - /// For best results, as shown in the sample code, both and - /// should be compile-time constants, so that the JIT compiler will be able to produce more efficient code. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool HasLookupFlag(uint table, int x, int min = 0) - { - // First, the input value is scaled down by the given minimum. - // This step will be skipped entirely if min is just the default of 0. - // The valid range is given by 32, which is the number of bits in the - // lookup table. The input value is first cast to uint so that if it was - // negative, the check will fail as well. Then, the result of this - // operation is used to compute a bitwise flag of either 0xFFFFFFFF if the - // input is accepted, or all 0 otherwise. The target bit is then extracted, - // and this value is combined with the previous mask. This is done so that - // if the shift was performed with a value that was too high, which has an - // undefined behavior and could produce a non-0 value, the mask will reset - // the final value anyway. This result is then unchecked-cast to a byte (as - // it is guaranteed to always be either 1 or 0), and then reinterpreted - // as a bool just like in the HasFlag method above, and then returned. - int i = x - min; - bool isInRange = (uint)i < 32u; - byte byteFlag = *(byte*)&isInRange; - int - negativeFlag = byteFlag - 1, - mask = ~negativeFlag, - shift = unchecked((int)((table >> i) & 1)), - and = shift & mask; - byte result = unchecked((byte)and); - bool valid = *(bool*)&result; - - return valid; - } - - /// - /// Checks whether the given value has any bytes that are set to 0. - /// That is, given a value, which has a total of 4 bytes, - /// it checks whether any of those have all the bits set to 0. - /// - /// The input value to check. - /// Whether has any bytes set to 0. - /// - /// This method contains no branches. - /// For more background on this subject, see . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool HasZeroByte(uint value) - { - return ((value - 0x0101_0101u) & ~value & 0x8080_8080u) != 0; - } - - /// - /// Checks whether the given value has any bytes that are set to 0. - /// This method mirrors , but with values. - /// - /// The input value to check. - /// Whether has any bytes set to 0. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool HasZeroByte(ulong value) - { - return ((value - 0x0101_0101_0101_0101ul) & ~value & 0x8080_8080_8080_8080ul) != 0; - } - - /// - /// Checks whether a byte in the input value matches a target value. - /// - /// The input value to check. - /// The target byte to look for. - /// Whether has any bytes set to . - /// - /// This method contains no branches. - /// For more info, see . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool HasByteEqualTo(uint value, byte target) - { - return HasZeroByte(value ^ (0x0101_0101u * target)); - } - - /// - /// Checks whether a byte in the input value matches a target value. - /// This method mirrors , but with values. - /// - /// The input value to check. - /// The target byte to look for. - /// Whether has any bytes set to . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool HasByteEqualTo(ulong value, byte target) - { - return HasZeroByte(value ^ (0x0101_0101_0101_0101u * target)); - } - - /// - /// Sets a bit to a specified value. - /// - /// The target value. - /// The position of the bit to set or clear (in [0, 31] range). - /// The value to assign to the target bit. - /// - /// Just like , this method doesn't validate - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SetFlag(ref uint value, int n, bool flag) - { - value = SetFlag(value, n, flag); - } - - /// - /// Sets a bit to a specified value. - /// - /// The input value. - /// The position of the bit to set or clear (in [0, 31] range). - /// The value to assign to the target bit. - /// An value equal to except for the -th bit. - /// - /// Just like , this method doesn't validate - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe uint SetFlag(uint value, int n, bool flag) - { - // Shift a bit left to the n-th position, negate the - // resulting value and perform an AND with the input value. - // This effectively clears the n-th bit of our input. - uint - bit = 1u << n, - not = ~bit, - and = value & not; - - // Reinterpret the flag as 1 or 0, and cast to uint, - // then we left shift the uint flag to the right position - // and perform an OR with the resulting value of the previous - // operation. This will always guaranteed to work, thanks to the - // initial code clearing that bit before setting it again. - bool copy = flag; - uint - flag32 = *(byte*)©, - shift = flag32 << n, - or = and | shift; - - return or; - } - - /// - /// Extracts a bit field range from a given value. - /// - /// The input value. - /// The initial index of the range to extract (in [0, 31] range). - /// The length of the range to extract (depends on ). - /// The value of the extracted range within . - /// - /// This method doesn't validate and . - /// If either parameter is not valid, the result will just be inconsistent. The method - /// should not be used to set all the bits at once, and it is not guaranteed to work in - /// that case, which would just be equivalent to assigning the value. - /// Additionally, no conditional branches are used to retrieve the range. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint ExtractRange(uint value, byte start, byte length) - { -#if NETCOREAPP3_1 || NET5_0 - if (Bmi1.IsSupported) - { - return Bmi1.BitFieldExtract(value, start, length); - } -#endif - - return (value >> start) & ((1u << length) - 1u); - } - - /// - /// Sets a bit field range within a target value. - /// - /// The target value. - /// The initial index of the range to extract (in [0, 31] range). - /// The length of the range to extract (depends on ). - /// The input flags to insert in the target range. - /// - /// Just like , this method doesn't validate the parameters - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SetRange(ref uint value, byte start, byte length, uint flags) - { - value = SetRange(value, start, length, flags); - } - - /// - /// Sets a bit field range within a target value. - /// - /// The initial value. - /// The initial index of the range to extract (in [0, 31] range). - /// The length of the range to extract (depends on ). - /// The input flags to insert in the target range. - /// The updated bit field value after setting the specified range. - /// - /// Just like , this method doesn't validate the parameters - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint SetRange(uint value, byte start, byte length, uint flags) - { - uint - highBits = (1u << length) - 1u, - loadMask = highBits << start, - storeMask = (flags & highBits) << start; - -#if NETCOREAPP3_1 || NET5_0 - if (Bmi1.IsSupported) - { - return Bmi1.AndNot(loadMask, value) | storeMask; - } -#endif - - return (~loadMask & value) | storeMask; - } - - /// - /// Checks whether or not a given bit is set. - /// - /// The input value. - /// The position of the bit to check (in [0, 63] range). - /// Whether or not the n-th bit is set. - /// - /// This method doesn't validate against the valid range. - /// If the parameter is not valid, the result will just be inconsistent. - /// Additionally, no conditional branches are used to retrieve the flag. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool HasFlag(ulong value, int n) - { - // Same logic as the uint version, see that for more info - byte flag = (byte)((value >> n) & 1); - - return *(bool*)&flag; - } - - /// - /// Checks whether or not a given bit is set in a given bitwise lookup table. - /// For more info, check the XML docs of the overload. - /// - /// The input lookup table to use. - /// The input value to check. - /// The minimum accepted value for (defaults to 0). - /// Whether or not the corresponding flag for is set in . - /// - /// For best results, as shown in the sample code, both and - /// should be compile-time constants, so that the JIT compiler will be able to produce more efficient code. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe bool HasLookupFlag(ulong table, int x, int min = 0) - { - int i = x - min; - bool isInRange = (uint)i < 64u; - byte byteFlag = *(byte*)&isInRange; - int - negativeFlag = byteFlag - 1, - mask = ~negativeFlag, - shift = unchecked((int)((table >> i) & 1)), - and = shift & mask; - byte result = unchecked((byte)and); - bool valid = *(bool*)&result; - - return valid; - } - - /// - /// Sets a bit to a specified value. - /// - /// The target value. - /// The position of the bit to set or clear (in [0, 63] range). - /// The value to assign to the target bit. - /// - /// Just like , this method doesn't validate - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SetFlag(ref ulong value, int n, bool flag) - { - value = SetFlag(value, n, flag); - } - - /// - /// Sets a bit to a specified value. - /// - /// The input value. - /// The position of the bit to set or clear (in [0, 63] range). - /// The value to assign to the target bit. - /// An value equal to except for the -th bit. - /// - /// Just like , this method doesn't validate - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ulong SetFlag(ulong value, int n, bool flag) - { - ulong - bit = 1ul << n, - not = ~bit, - and = value & not; - bool copy = flag; - ulong flag64 = *(byte*)©, - shift = flag64 << n, - or = and | shift; - - return or; - } - - /// - /// Extracts a bit field range from a given value. - /// - /// The input value. - /// The initial index of the range to extract (in [0, 63] range). - /// The length of the range to extract (depends on ). - /// The value of the extracted range within . - /// - /// This method doesn't validate and . - /// If either parameter is not valid, the result will just be inconsistent. The method - /// should not be used to set all the bits at once, and it is not guaranteed to work in - /// that case, which would just be equivalent to assigning the value. - /// Additionally, no conditional branches are used to retrieve the range. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong ExtractRange(ulong value, byte start, byte length) - { -#if NETCOREAPP3_1 || NET5_0 - if (Bmi1.X64.IsSupported) - { - return Bmi1.X64.BitFieldExtract(value, start, length); - } -#endif - - return (value >> start) & ((1ul << length) - 1ul); - } - - /// - /// Sets a bit field range within a target value. - /// - /// The target value. - /// The initial index of the range to extract (in [0, 63] range). - /// The length of the range to extract (depends on ). - /// The input flags to insert in the target range. - /// - /// Just like , this method doesn't validate the parameters - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SetRange(ref ulong value, byte start, byte length, ulong flags) - { - value = SetRange(value, start, length, flags); - } - - /// - /// Sets a bit field range within a target value. - /// - /// The initial value. - /// The initial index of the range to extract (in [0, 63] range). - /// The length of the range to extract (depends on ). - /// The input flags to insert in the target range. - /// The updated bit field value after setting the specified range. - /// - /// Just like , this method doesn't validate the parameters - /// and does not contain branching instructions, so it's well suited for use in tight loops as well. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong SetRange(ulong value, byte start, byte length, ulong flags) - { - ulong - highBits = (1ul << length) - 1ul, - loadMask = highBits << start, - storeMask = (flags & highBits) << start; - -#if NETCOREAPP3_1 || NET5_0 - if (Bmi1.X64.IsSupported) - { - return Bmi1.X64.AndNot(loadMask, value) | storeMask; - } -#endif - - return (~loadMask & value) | storeMask; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/HashCode{T}.cs b/Microsoft.Toolkit.HighPerformance/Helpers/HashCode{T}.cs deleted file mode 100644 index 8b5e59ce34e..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/HashCode{T}.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if !NETSTANDARD1_4 - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Microsoft.Toolkit.HighPerformance.Helpers.Internals; -#if SPAN_RUNTIME_SUPPORT -using RuntimeHelpers = System.Runtime.CompilerServices.RuntimeHelpers; -#else -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; -#endif - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Combines the hash code of sequences of values into a single hash code. - /// - /// The type of values to hash. - /// - /// The hash codes returned by the method are only guaranteed to be repeatable for - /// the current execution session, just like with the available APIs.In other words, - /// hashing the same collection multiple times in the same process will always - /// result in the same hash code, while the same collection being hashed again from another process - /// (or another instance of the same process) is not guaranteed to result in the same final value. - /// For more info, see . - /// - public struct HashCode - where T : notnull - { - /// - /// Gets a content hash from the input instance using the xxHash32 algorithm. - /// - /// The input instance - /// The xxHash32 value for the input instance - /// The xxHash32 is only guaranteed to be deterministic within the scope of a single app execution - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Combine(ReadOnlySpan span) - { - int hash = CombineValues(span); - - return HashCode.Combine(hash); - } - - /// - /// Gets a content hash from the input instance. - /// - /// The input instance - /// The hash code for the input instance - /// The returned hash code is not processed through APIs. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int CombineValues(ReadOnlySpan span) - { - ref T r0 = ref MemoryMarshal.GetReference(span); - - // If typeof(T) is not unmanaged, iterate over all the items one by one. - // This check is always known in advance either by the JITter or by the AOT - // compiler, so this branch will never actually be executed by the code. - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - return SpanHelper.GetDjb2HashCode(ref r0, (nint)(uint)span.Length); - } - - // Get the info for the target memory area to process. - // The line below is computing the total byte size for the span, - // and we cast both input factors to uint first to avoid sign extensions - // (they're both guaranteed to always be positive values), and to let the - // JIT avoid the 64 bit computation entirely when running in a 32 bit - // process. In that case it will just compute the byte size as a 32 bit - // multiplication with overflow, which is guaranteed never to happen anyway. - ref byte rb = ref Unsafe.As(ref r0); - nint length = (nint)((uint)span.Length * (uint)Unsafe.SizeOf()); - - return SpanHelper.GetDjb2LikeByteHash(ref rb, length); - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/BitOperations.cs b/Microsoft.Toolkit.HighPerformance/Helpers/Internals/BitOperations.cs deleted file mode 100644 index 9d326ec1c14..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/BitOperations.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -#if NETCOREAPP3_1 || NET5_0 -using static System.Numerics.BitOperations; -#endif - -namespace Microsoft.Toolkit.HighPerformance.Helpers.Internals -{ - /// - /// Utility methods for intrinsic bit-twiddling operations. The methods use hardware intrinsics - /// when available on the underlying platform, otherwise they use optimized software fallbacks. - /// - internal static class BitOperations - { - /// - /// Rounds up an value to a power of 2. - /// - /// The input value to round up. - /// The smallest power of two greater than or equal to . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int RoundUpPowerOfTwo(int x) - { -#if NETCOREAPP3_1 || NET5_0 - return 1 << (32 - LeadingZeroCount((uint)(x - 1))); -#else - x--; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x++; - - return x; -#endif - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/RefEnumerableHelper.cs b/Microsoft.Toolkit.HighPerformance/Helpers/Internals/RefEnumerableHelper.cs deleted file mode 100644 index e054fbd8bdf..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/RefEnumerableHelper.cs +++ /dev/null @@ -1,267 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Helpers.Internals -{ - /// - /// Helpers to process sequences of values by reference with a given step. - /// - internal static class RefEnumerableHelper - { - /// - /// Clears a target memory area. - /// - /// The type of values to clear. - /// A reference to the start of the memory area. - /// The number of items in the memory area. - /// The number of items between each consecutive target value. - public static void Clear(ref T r0, nint length, nint step) - { - nint offset = 0; - - // Main loop with 8 unrolled iterations - while (length >= 8) - { - Unsafe.Add(ref r0, offset) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - - length -= 8; - offset += step; - } - - if (length >= 4) - { - Unsafe.Add(ref r0, offset) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - Unsafe.Add(ref r0, offset += step) = default!; - - length -= 4; - offset += step; - } - - // Clear the remaining values - while (length > 0) - { - Unsafe.Add(ref r0, offset) = default!; - - length -= 1; - offset += step; - } - } - - /// - /// Copies a sequence of discontiguous items from one memory area to another. - /// - /// The type of items to copy. - /// The source reference to copy from. - /// The target reference to copy to. - /// The total number of items to copy. - /// The step between consecutive items in the memory area pointed to by . - public static void CopyTo(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep) - { - nint - sourceOffset = 0, - destinationOffset = 0; - - while (length >= 8) - { - Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset); - Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 4) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 5) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 6) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 7) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - - length -= 8; - sourceOffset += sourceStep; - destinationOffset += 8; - } - - if (length >= 4) - { - Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset); - Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - - length -= 4; - sourceOffset += sourceStep; - destinationOffset += 4; - } - - while (length > 0) - { - Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); - - length -= 1; - sourceOffset += sourceStep; - destinationOffset += 1; - } - } - - /// - /// Copies a sequence of discontiguous items from one memory area to another. - /// - /// The type of items to copy. - /// The source reference to copy from. - /// The target reference to copy to. - /// The total number of items to copy. - /// The step between consecutive items in the memory area pointed to by . - /// The step between consecutive items in the memory area pointed to by . - public static void CopyTo(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep, nint destinationStep) - { - nint - sourceOffset = 0, - destinationOffset = 0; - - while (length >= 8) - { - Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - - length -= 8; - sourceOffset += sourceStep; - destinationOffset += destinationStep; - } - - if (length >= 4) - { - Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - Unsafe.Add(ref destinationRef, destinationOffset += destinationStep) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep); - - length -= 4; - sourceOffset += sourceStep; - destinationOffset += destinationStep; - } - - while (length > 0) - { - Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); - - length -= 1; - sourceOffset += sourceStep; - destinationOffset += destinationStep; - } - } - - /// - /// Copies a sequence of discontiguous items from one memory area to another. This mirrors - /// , but refers to instead. - /// - /// The type of items to copy. - /// The source reference to copy from. - /// The target reference to copy to. - /// The total number of items to copy. - /// The step between consecutive items in the memory area pointed to by . - public static void CopyFrom(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep) - { - nint - sourceOffset = 0, - destinationOffset = 0; - - while (length >= 8) - { - Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 1); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 2); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 3); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 4); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 5); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 6); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 7); - - length -= 8; - sourceOffset += 8; - destinationOffset += sourceStep; - } - - if (length >= 4) - { - Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 1); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 2); - Unsafe.Add(ref destinationRef, destinationOffset += sourceStep) = Unsafe.Add(ref sourceRef, sourceOffset + 3); - - length -= 4; - sourceOffset += 4; - destinationOffset += sourceStep; - } - - while (length > 0) - { - Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset); - - length -= 1; - sourceOffset += 1; - destinationOffset += sourceStep; - } - } - - /// - /// Fills a target memory area. - /// - /// The type of values to fill. - /// A reference to the start of the memory area. - /// The number of items in the memory area. - /// The number of items between each consecutive target value. - /// The value to assign to every item in the target memory area. - public static void Fill(ref T r0, nint length, nint step, T value) - { - nint offset = 0; - - while (length >= 8) - { - Unsafe.Add(ref r0, offset) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - - length -= 8; - offset += step; - } - - if (length >= 4) - { - Unsafe.Add(ref r0, offset) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - Unsafe.Add(ref r0, offset += step) = value; - - length -= 4; - offset += step; - } - - while (length > 0) - { - Unsafe.Add(ref r0, offset) = value; - - length -= 1; - offset += step; - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/RuntimeHelpers.cs b/Microsoft.Toolkit.HighPerformance/Helpers/Internals/RuntimeHelpers.cs deleted file mode 100644 index 49817edc31a..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/RuntimeHelpers.cs +++ /dev/null @@ -1,304 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1512 - -// The portable implementation in this type is originally from CoreFX. -// See https://github.com/dotnet/corefx/blob/release/2.1/src/System.Memory/src/System/SpanHelpers.cs. - -using System; -using System.Diagnostics.Contracts; -#if !SPAN_RUNTIME_SUPPORT -using System.Reflection; -#endif -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Helpers.Internals -{ - /// - /// A helper class that with utility methods for dealing with references, and other low-level details. - /// It also contains some APIs that act as polyfills for .NET Standard 2.0 and below. - /// - internal static class RuntimeHelpers - { - /// - /// Converts a length of items from one size to another (rounding towards zero). - /// - /// The source type of items. - /// The target type of items. - /// The input length to convert. - /// The converted length for the specified argument and types. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int ConvertLength(int length) - where TFrom : unmanaged - where TTo : unmanaged - { - if (sizeof(TFrom) == sizeof(TTo)) - { - return length; - } - else if (sizeof(TFrom) == 1) - { - return (int)((uint)length / (uint)sizeof(TTo)); - } - else - { - ulong targetLength = (ulong)(uint)length * (uint)sizeof(TFrom) / (uint)sizeof(TTo); - - return checked((int)targetLength); - } - } - - /// - /// Gets the length of a given array as a native integer. - /// - /// The type of values in the array. - /// The input instance. - /// The total length of as a native integer. - /// - /// This method is needed because this expression is not inlined correctly if the target array - /// is only visible as a non-generic instance, because the C# compiler will - /// not be able to emit the opcode instead of calling the right method. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint GetArrayNativeLength(T[] array) - { -#if NETSTANDARD1_4 - // .NET Standard 1.4 doesn't include the API to get the long length, so - // we just cast the length and throw in case the array is larger than - // int.MaxValue. There's not much we can do in this specific case. - return (nint)(uint)array.Length; -#else - return (nint)array.LongLength; -#endif - } - - /// - /// Gets the length of a given array as a native integer. - /// - /// The input instance. - /// The total length of as a native integer. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint GetArrayNativeLength(Array array) - { -#if NETSTANDARD1_4 - return (nint)(uint)array.Length; -#else - return (nint)array.LongLength; -#endif - } - - /// - /// Gets the byte offset to the first element in a SZ array. - /// - /// The type of values in the array. - /// The byte offset to the first element in a SZ array. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetArrayDataByteOffset() - { - return TypeInfo.ArrayDataByteOffset; - } - - /// - /// Gets the byte offset to the first element in a 2D array. - /// - /// The type of values in the array. - /// The byte offset to the first element in a 2D array. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetArray2DDataByteOffset() - { - return TypeInfo.Array2DDataByteOffset; - } - - /// - /// Gets the byte offset to the first element in a 3D array. - /// - /// The type of values in the array. - /// The byte offset to the first element in a 3D array. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetArray3DDataByteOffset() - { - return TypeInfo.Array3DDataByteOffset; - } - -#if !SPAN_RUNTIME_SUPPORT - /// - /// Gets a byte offset describing a portable pinnable reference. This can either be an - /// interior pointer into some object data (described with a valid reference - /// and a reference to some of its data), or a raw pointer (described with a - /// reference to an , and a reference that is assumed to refer to pinned data). - /// - /// The type of field being referenced. - /// The input hosting the target field. - /// A reference to a target field of type within . - /// - /// The value representing the offset to the target field from the start of the object data - /// for the parameter , or the value of the raw pointer passed as a tracked reference. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe IntPtr GetObjectDataOrReferenceByteOffset(object? obj, ref T data) - { - if (obj is null) - { - return (IntPtr)Unsafe.AsPointer(ref data); - } - - return ObjectMarshal.DangerousGetObjectDataByteOffset(obj, ref data); - } - - /// - /// Gets a reference from data describing a portable pinnable reference. This can either be an - /// interior pointer into some object data (described with a valid reference - /// and a byte offset into its data), or a raw pointer (described with a - /// reference to an , and a byte offset representing the value of the raw pointer). - /// - /// The type of reference to retrieve. - /// The input hosting the target field. - /// The input byte offset for the reference to retrieve. - /// A reference matching the given parameters. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ref T GetObjectDataAtOffsetOrPointerReference(object? obj, IntPtr offset) - { - if (obj is null) - { - return ref Unsafe.AsRef((void*)offset); - } - - return ref ObjectMarshal.DangerousGetObjectDataReferenceAt(obj, offset); - } - - /// - /// Checks whether or not a given type is a reference type or contains references. - /// - /// The type to check. - /// Whether or not respects the constraint. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsReferenceOrContainsReferences() - { - return TypeInfo.IsReferenceOrContainsReferences; - } - - /// - /// Implements the logic for . - /// - /// The current type to check. - /// Whether or not is a reference type or contains references. - [Pure] - private static bool IsReferenceOrContainsReferences(Type type) - { - // Common case, for primitive types - if (type.GetTypeInfo().IsPrimitive) - { - return false; - } - - if (!type.GetTypeInfo().IsValueType) - { - return true; - } - - // Check if the type is Nullable - if (Nullable.GetUnderlyingType(type) is Type nullableType) - { - type = nullableType; - } - - if (type.GetTypeInfo().IsEnum) - { - return false; - } - - // Complex struct, recursively inspect all fields - foreach (FieldInfo field in type.GetTypeInfo().DeclaredFields) - { - if (field.IsStatic) - { - continue; - } - - if (IsReferenceOrContainsReferences(field.FieldType)) - { - return true; - } - } - - return false; - } -#endif - - /// - /// A private generic class to preload type info for arbitrary runtime types. - /// - /// The type to load info for. - private static class TypeInfo - { - /// - /// The byte offset to the first element in a SZ array. - /// - public static readonly IntPtr ArrayDataByteOffset = MeasureArrayDataByteOffset(); - - /// - /// The byte offset to the first element in a 2D array. - /// - public static readonly IntPtr Array2DDataByteOffset = MeasureArray2DDataByteOffset(); - - /// - /// The byte offset to the first element in a 3D array. - /// - public static readonly IntPtr Array3DDataByteOffset = MeasureArray3DDataByteOffset(); - -#if !SPAN_RUNTIME_SUPPORT - /// - /// Indicates whether does not respect the constraint. - /// - public static readonly bool IsReferenceOrContainsReferences = IsReferenceOrContainsReferences(typeof(T)); -#endif - - /// - /// Computes the value for . - /// - /// The value of for the current runtime. - [Pure] - private static IntPtr MeasureArrayDataByteOffset() - { - var array = new T[1]; - - return ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array[0]); - } - - /// - /// Computes the value for . - /// - /// The value of for the current runtime. - [Pure] - private static IntPtr MeasureArray2DDataByteOffset() - { - var array = new T[1, 1]; - - return ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array[0, 0]); - } - - /// - /// Computes the value for . - /// - /// The value of for the current runtime. - [Pure] - private static IntPtr MeasureArray3DDataByteOffset() - { - var array = new T[1, 1, 1]; - - return ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array[0, 0, 0]); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/SpanHelper.Count.cs b/Microsoft.Toolkit.HighPerformance/Helpers/Internals/SpanHelper.Count.cs deleted file mode 100644 index 8122a4bca46..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/SpanHelper.Count.cs +++ /dev/null @@ -1,368 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Helpers.Internals -{ - /// - /// Helpers to process sequences of values by reference. - /// - internal static partial class SpanHelper - { - /// - /// Counts the number of occurrences of a given value into a target search space. - /// - /// A reference to the start of the search space. - /// The number of items in the search space. - /// The value to look for. - /// The type of value to look for. - /// The number of occurrences of in the search space - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint Count(ref T r0, nint length, T value) - where T : IEquatable - { - if (!Vector.IsHardwareAccelerated) - { - return CountSequential(ref r0, length, value); - } - - // Special vectorized version when using a supported type - if (typeof(T) == typeof(byte) || - typeof(T) == typeof(sbyte) || - typeof(T) == typeof(bool)) - { - ref sbyte r1 = ref Unsafe.As(ref r0); - sbyte target = Unsafe.As(ref value); - - return CountSimd(ref r1, length, target); - } - - if (typeof(T) == typeof(char) || - typeof(T) == typeof(ushort) || - typeof(T) == typeof(short)) - { - ref short r1 = ref Unsafe.As(ref r0); - short target = Unsafe.As(ref value); - - return CountSimd(ref r1, length, target); - } - - if (typeof(T) == typeof(int) || - typeof(T) == typeof(uint)) - { - ref int r1 = ref Unsafe.As(ref r0); - int target = Unsafe.As(ref value); - - return CountSimd(ref r1, length, target); - } - - if (typeof(T) == typeof(long) || - typeof(T) == typeof(ulong)) - { - ref long r1 = ref Unsafe.As(ref r0); - long target = Unsafe.As(ref value); - - return CountSimd(ref r1, length, target); - } - - return CountSequential(ref r0, length, value); - } - - /// - /// Implements with a sequential search. - /// - [Pure] - private static nint CountSequential(ref T r0, nint length, T value) - where T : IEquatable - { - nint - result = 0, - offset = 0; - - // Main loop with 8 unrolled iterations - while (length >= 8) - { - result += Unsafe.Add(ref r0, offset + 0).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 1).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 2).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 3).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 4).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 5).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 6).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 7).Equals(value).ToByte(); - - length -= 8; - offset += 8; - } - - if (length >= 4) - { - result += Unsafe.Add(ref r0, offset + 0).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 1).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 2).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 3).Equals(value).ToByte(); - - length -= 4; - offset += 4; - } - - // Iterate over the remaining values and count those that match - while (length > 0) - { - result += Unsafe.Add(ref r0, offset).Equals(value).ToByte(); - - length -= 1; - offset += 1; - } - - return result; - } - - /// - /// Implements with a vectorized search. - /// - [Pure] - private static nint CountSimd(ref T r0, nint length, T value) - where T : unmanaged, IEquatable - { - nint - result = 0, - offset = 0; - - // Skip the initialization overhead if there are not enough items - if (length >= Vector.Count) - { - var vc = new Vector(value); - - do - { - // Calculate the maximum sequential area that can be processed in - // one pass without the risk of numeric overflow in the dot product - // to sum the partial results. We also backup the current offset to - // be able to track how many items have been processed, which lets - // us avoid updating a third counter (length) in the loop body. - nint - max = GetUpperBound(), - chunkLength = length <= max ? length : max, - initialOffset = offset; - - var partials = Vector.Zero; - - // Unrolled vectorized loop, with 8 unrolled iterations. We only run this when the - // current type T is at least 2 bytes in size, otherwise the average chunk length - // would always be too small to be able to trigger the unrolled loop, and the overall - // performance would just be slightly worse due to the additional conditional branches. - if (typeof(T) != typeof(sbyte)) - { - while (chunkLength >= Vector.Count * 8) - { - ref T ri0 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 0)); - var vi0 = Unsafe.As>(ref ri0); - var ve0 = Vector.Equals(vi0, vc); - - partials -= ve0; - - ref T ri1 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 1)); - var vi1 = Unsafe.As>(ref ri1); - var ve1 = Vector.Equals(vi1, vc); - - partials -= ve1; - - ref T ri2 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 2)); - var vi2 = Unsafe.As>(ref ri2); - var ve2 = Vector.Equals(vi2, vc); - - partials -= ve2; - - ref T ri3 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 3)); - var vi3 = Unsafe.As>(ref ri3); - var ve3 = Vector.Equals(vi3, vc); - - partials -= ve3; - - ref T ri4 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 4)); - var vi4 = Unsafe.As>(ref ri4); - var ve4 = Vector.Equals(vi4, vc); - - partials -= ve4; - - ref T ri5 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 5)); - var vi5 = Unsafe.As>(ref ri5); - var ve5 = Vector.Equals(vi5, vc); - - partials -= ve5; - - ref T ri6 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 6)); - var vi6 = Unsafe.As>(ref ri6); - var ve6 = Vector.Equals(vi6, vc); - - partials -= ve6; - - ref T ri7 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 7)); - var vi7 = Unsafe.As>(ref ri7); - var ve7 = Vector.Equals(vi7, vc); - - partials -= ve7; - - chunkLength -= Vector.Count * 8; - offset += Vector.Count * 8; - } - } - - while (chunkLength >= Vector.Count) - { - ref T ri = ref Unsafe.Add(ref r0, offset); - - // Load the current Vector register, and then use - // Vector.Equals to check for matches. This API sets the - // values corresponding to matching pairs to all 1s. - // Since the input type is guaranteed to always be signed, - // this means that a value with all 1s represents -1, as - // signed numbers are represented in two's complement. - // So we can just subtract this intermediate value to the - // partial results, which effectively sums 1 for each match. - var vi = Unsafe.As>(ref ri); - var ve = Vector.Equals(vi, vc); - - partials -= ve; - - chunkLength -= Vector.Count; - offset += Vector.Count; - } - - result += CastToNativeInt(Vector.Dot(partials, Vector.One)); - length -= offset - initialOffset; - } - while (length >= Vector.Count); - } - - // Optional 8 unrolled iterations. This is only done when a single SIMD - // register can contain over 8 values of the current type, as otherwise - // there could never be enough items left after the vectorized path - if (Vector.Count > 8 && - length >= 8) - { - result += Unsafe.Add(ref r0, offset + 0).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 1).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 2).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 3).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 4).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 5).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 6).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 7).Equals(value).ToByte(); - - length -= 8; - offset += 8; - } - - // Optional 4 unrolled iterations - if (Vector.Count > 4 && - length >= 4) - { - result += Unsafe.Add(ref r0, offset + 0).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 1).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 2).Equals(value).ToByte(); - result += Unsafe.Add(ref r0, offset + 3).Equals(value).ToByte(); - - length -= 4; - offset += 4; - } - - // Iterate over the remaining values and count those that match - while (length > 0) - { - result += Unsafe.Add(ref r0, offset).Equals(value).ToByte(); - - length -= 1; - offset += 1; - } - - return result; - } - - /// - /// Gets the upper bound for partial sums with a given parameter. - /// - /// The type argument currently in use. - /// The native value representing the upper bound. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe nint GetUpperBound() - where T : unmanaged - { - if (typeof(T) == typeof(sbyte)) - { - return sbyte.MaxValue; - } - - if (typeof(T) == typeof(short)) - { - return short.MaxValue; - } - - if (typeof(T) == typeof(int)) - { - return int.MaxValue; - } - - if (typeof(T) == typeof(long)) - { - if (sizeof(nint) == sizeof(int)) - { - return int.MaxValue; - } - - // If we are on a 64 bit architecture and we are counting with a SIMD vector of 64 - // bit values, we can use long.MaxValue as the upper bound, as a native integer will - // be able to contain such a value with no overflows. This will allow the count tight - // loop to process all the items in the target area in a single pass (except the mod). - // The (void*) cast is necessary to ensure the right constant is produced on runtimes - // before .NET 5 that don't natively support C# 9. For instance, removing that (void*) - // cast results in the value 0xFFFFFFFFFFFFFFFF (-1) instead of 0x7FFFFFFFFFFFFFFFF. - return (nint)(void*)long.MaxValue; - } - - throw null!; - } - - /// - /// Casts a value of a given type to a native . - /// - /// The input type to cast. - /// The input value to cast to native . - /// The native cast of . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static nint CastToNativeInt(T value) - where T : unmanaged - { - if (typeof(T) == typeof(sbyte)) - { - return (byte)(sbyte)(object)value; - } - - if (typeof(T) == typeof(short)) - { - return (ushort)(short)(object)value; - } - - if (typeof(T) == typeof(int)) - { - return (nint)(uint)(int)(object)value; - } - - if (typeof(T) == typeof(long)) - { - return (nint)(ulong)(long)(object)value; - } - - throw null!; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/SpanHelper.Hash.cs b/Microsoft.Toolkit.HighPerformance/Helpers/Internals/SpanHelper.Hash.cs deleted file mode 100644 index a113d4c130d..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/Internals/SpanHelper.Hash.cs +++ /dev/null @@ -1,315 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Helpers.Internals -{ - /// - /// Helpers to process sequences of values by reference. - /// - internal static partial class SpanHelper - { - /// - /// Calculates the djb2 hash for the target sequence of items of a given type. - /// - /// The type of items to hash. - /// The reference to the target memory area to hash. - /// The number of items to hash. - /// The Djb2 value for the input sequence of items. - [Pure] - public static int GetDjb2HashCode(ref T r0, nint length) - where T : notnull - { - int hash = 5381; - nint offset = 0; - - while (length >= 8) - { - // Doing a left shift by 5 and adding is equivalent to multiplying by 33. - // This is preferred for performance reasons, as when working with integer - // values most CPUs have higher latency for multiplication operations - // compared to a simple shift and add. For more info on this, see the - // details for imul, shl, add: https://gmplib.org/~tege/x86-timing.pdf. - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 0).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 1).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 2).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 3).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 4).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 5).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 6).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 7).GetHashCode()); - - length -= 8; - offset += 8; - } - - if (length >= 4) - { - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 0).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 1).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 2).GetHashCode()); - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset + 3).GetHashCode()); - - length -= 4; - offset += 4; - } - - while (length > 0) - { - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset).GetHashCode()); - - length -= 1; - offset += 1; - } - - return hash; - } - - /// - /// Gets a content hash from a given memory area. - /// - /// A reference to the start of the memory area. - /// The size in bytes of the memory area. - /// The hash code for the contents of the source memory area. - /// - /// While this method is similar to and can in some cases - /// produce the same output for a given memory area, it is not guaranteed to always be that way. - /// This is because this method can use SIMD instructions if possible, which can cause a computed - /// hash to differ for the same data, if processed on different machines with different CPU features. - /// The advantage of this method is that when SIMD instructions are available, it performs much - /// faster than , as it can parallelize much of the workload. - /// - [Pure] - public static unsafe int GetDjb2LikeByteHash(ref byte r0, nint length) - { - int hash = 5381; - nint offset = 0; - - // Check whether SIMD instructions are supported, and also check - // whether we have enough data to perform at least one unrolled - // iteration of the vectorized path. This heuristics is to balance - // the overhead of loading the constant values in the two registers, - // and the final loop to combine the partial hash values. - // Note that even when we use the vectorized path we don't need to do - // any preprocessing to try to get memory aligned, as that would cause - // the hash codes to potentially be different for the same data. - if (Vector.IsHardwareAccelerated && - length >= (Vector.Count << 3)) - { - var vh = new Vector(5381); - var v33 = new Vector(33); - - // First vectorized loop, with 8 unrolled iterations. - // Assuming 256-bit registers (AVX2), a total of 256 bytes are processed - // per iteration, with the partial hashes being accumulated for later use. - while (length >= (Vector.Count << 3)) - { - ref byte ri0 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 0)); - var vi0 = Unsafe.ReadUnaligned>(ref ri0); - var vp0 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp0, vi0); - - ref byte ri1 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 1)); - var vi1 = Unsafe.ReadUnaligned>(ref ri1); - var vp1 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp1, vi1); - - ref byte ri2 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 2)); - var vi2 = Unsafe.ReadUnaligned>(ref ri2); - var vp2 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp2, vi2); - - ref byte ri3 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 3)); - var vi3 = Unsafe.ReadUnaligned>(ref ri3); - var vp3 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp3, vi3); - - ref byte ri4 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 4)); - var vi4 = Unsafe.ReadUnaligned>(ref ri4); - var vp4 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp4, vi4); - - ref byte ri5 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 5)); - var vi5 = Unsafe.ReadUnaligned>(ref ri5); - var vp5 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp5, vi5); - - ref byte ri6 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 6)); - var vi6 = Unsafe.ReadUnaligned>(ref ri6); - var vp6 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp6, vi6); - - ref byte ri7 = ref Unsafe.Add(ref r0, offset + (Vector.Count * 7)); - var vi7 = Unsafe.ReadUnaligned>(ref ri7); - var vp7 = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp7, vi7); - - length -= Vector.Count << 3; - offset += Vector.Count << 3; - } - - // When this loop is reached, there are up to 255 bytes left (on AVX2). - // Each iteration processed an additional 32 bytes and accumulates the results. - while (length >= Vector.Count) - { - ref byte ri = ref Unsafe.Add(ref r0, offset); - var vi = Unsafe.ReadUnaligned>(ref ri); - var vp = Vector.Multiply(vh, v33); - vh = Vector.Xor(vp, vi); - - length -= Vector.Count; - offset += Vector.Count; - } - - // Combine the partial hash values in each position. - // The loop below should automatically be unrolled by the JIT. - for (var j = 0; j < Vector.Count; j++) - { - hash = unchecked(((hash << 5) + hash) ^ vh[j]); - } - } - else - { - // Only use the loop working with 64-bit values if we are on a - // 64-bit processor, otherwise the result would be much slower. - // Each unrolled iteration processes 64 bytes. - if (sizeof(nint) == sizeof(ulong)) - { - while (length >= (sizeof(ulong) << 3)) - { - ref byte ri0 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 0)); - var value0 = Unsafe.ReadUnaligned(ref ri0); - hash = unchecked(((hash << 5) + hash) ^ (int)value0 ^ (int)(value0 >> 32)); - - ref byte ri1 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 1)); - var value1 = Unsafe.ReadUnaligned(ref ri1); - hash = unchecked(((hash << 5) + hash) ^ (int)value1 ^ (int)(value1 >> 32)); - - ref byte ri2 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 2)); - var value2 = Unsafe.ReadUnaligned(ref ri2); - hash = unchecked(((hash << 5) + hash) ^ (int)value2 ^ (int)(value2 >> 32)); - - ref byte ri3 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 3)); - var value3 = Unsafe.ReadUnaligned(ref ri3); - hash = unchecked(((hash << 5) + hash) ^ (int)value3 ^ (int)(value3 >> 32)); - - ref byte ri4 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 4)); - var value4 = Unsafe.ReadUnaligned(ref ri4); - hash = unchecked(((hash << 5) + hash) ^ (int)value4 ^ (int)(value4 >> 32)); - - ref byte ri5 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 5)); - var value5 = Unsafe.ReadUnaligned(ref ri5); - hash = unchecked(((hash << 5) + hash) ^ (int)value5 ^ (int)(value5 >> 32)); - - ref byte ri6 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 6)); - var value6 = Unsafe.ReadUnaligned(ref ri6); - hash = unchecked(((hash << 5) + hash) ^ (int)value6 ^ (int)(value6 >> 32)); - - ref byte ri7 = ref Unsafe.Add(ref r0, offset + (sizeof(ulong) * 7)); - var value7 = Unsafe.ReadUnaligned(ref ri7); - hash = unchecked(((hash << 5) + hash) ^ (int)value7 ^ (int)(value7 >> 32)); - - length -= sizeof(ulong) << 3; - offset += sizeof(ulong) << 3; - } - } - - // Each unrolled iteration processes 32 bytes - while (length >= (sizeof(uint) << 3)) - { - ref byte ri0 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 0)); - var value0 = Unsafe.ReadUnaligned(ref ri0); - hash = unchecked(((hash << 5) + hash) ^ (int)value0); - - ref byte ri1 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 1)); - var value1 = Unsafe.ReadUnaligned(ref ri1); - hash = unchecked(((hash << 5) + hash) ^ (int)value1); - - ref byte ri2 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 2)); - var value2 = Unsafe.ReadUnaligned(ref ri2); - hash = unchecked(((hash << 5) + hash) ^ (int)value2); - - ref byte ri3 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 3)); - var value3 = Unsafe.ReadUnaligned(ref ri3); - hash = unchecked(((hash << 5) + hash) ^ (int)value3); - - ref byte ri4 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 4)); - var value4 = Unsafe.ReadUnaligned(ref ri4); - hash = unchecked(((hash << 5) + hash) ^ (int)value4); - - ref byte ri5 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 5)); - var value5 = Unsafe.ReadUnaligned(ref ri5); - hash = unchecked(((hash << 5) + hash) ^ (int)value5); - - ref byte ri6 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 6)); - var value6 = Unsafe.ReadUnaligned(ref ri6); - hash = unchecked(((hash << 5) + hash) ^ (int)value6); - - ref byte ri7 = ref Unsafe.Add(ref r0, offset + (sizeof(uint) * 7)); - var value7 = Unsafe.ReadUnaligned(ref ri7); - hash = unchecked(((hash << 5) + hash) ^ (int)value7); - - length -= sizeof(uint) << 3; - offset += sizeof(uint) << 3; - } - } - - // At this point (assuming AVX2), there will be up to 31 bytes - // left, both for the vectorized and non vectorized paths. - // That number would go up to 63 on AVX512 systems, in which case it is - // still useful to perform this last loop unrolling. - if (length >= (sizeof(ushort) << 3)) - { - ref byte ri0 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 0)); - var value0 = Unsafe.ReadUnaligned(ref ri0); - hash = unchecked(((hash << 5) + hash) ^ value0); - - ref byte ri1 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 1)); - var value1 = Unsafe.ReadUnaligned(ref ri1); - hash = unchecked(((hash << 5) + hash) ^ value1); - - ref byte ri2 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 2)); - var value2 = Unsafe.ReadUnaligned(ref ri2); - hash = unchecked(((hash << 5) + hash) ^ value2); - - ref byte ri3 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 3)); - var value3 = Unsafe.ReadUnaligned(ref ri3); - hash = unchecked(((hash << 5) + hash) ^ value3); - - ref byte ri4 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 4)); - var value4 = Unsafe.ReadUnaligned(ref ri4); - hash = unchecked(((hash << 5) + hash) ^ value4); - - ref byte ri5 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 5)); - var value5 = Unsafe.ReadUnaligned(ref ri5); - hash = unchecked(((hash << 5) + hash) ^ value5); - - ref byte ri6 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 6)); - var value6 = Unsafe.ReadUnaligned(ref ri6); - hash = unchecked(((hash << 5) + hash) ^ value6); - - ref byte ri7 = ref Unsafe.Add(ref r0, offset + (sizeof(ushort) * 7)); - var value7 = Unsafe.ReadUnaligned(ref ri7); - hash = unchecked(((hash << 5) + hash) ^ value7); - - length -= sizeof(ushort) << 3; - offset += sizeof(ushort) << 3; - } - - // Handle the leftover items - while (length > 0) - { - hash = unchecked(((hash << 5) + hash) ^ Unsafe.Add(ref r0, offset)); - - length -= 1; - offset += 1; - } - - return hash; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ObjectMarshal.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ObjectMarshal.cs deleted file mode 100644 index 9c77a007948..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ObjectMarshal.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers for working with instances. - /// - public static class ObjectMarshal - { - /// - /// Calculates the byte offset to a specific field within a given . - /// - /// The type of field being referenced. - /// The input hosting the target field. - /// A reference to a target field of type within . - /// - /// The value representing the offset to the target field from the start of the object data - /// for the parameter . The offset is in relation to the first usable byte after the method table. - /// - /// The input parameters are not validated, and it's responsibility of the caller to ensure that - /// the reference is actually pointing to a memory location within . - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr DangerousGetObjectDataByteOffset(object obj, ref T data) - { - var rawObj = Unsafe.As(obj)!; - ref byte r0 = ref rawObj.Data; - ref byte r1 = ref Unsafe.As(ref data); - - return Unsafe.ByteOffset(ref r0, ref r1); - } - - /// - /// Gets a reference to data within a given at a specified offset. - /// - /// The type of reference to retrieve. - /// The input hosting the target field. - /// The input byte offset for the reference to retrieve. - /// A reference at a specified offset within . - /// - /// None of the input arguments is validated, and it is responsibility of the caller to ensure they are valid. - /// In particular, using an invalid offset might cause the retrieved reference to be misaligned with the - /// desired data, which would break the type system. Or, if the offset causes the retrieved reference to point - /// to a memory location outside of the input instance, that might lead to runtime crashes. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousGetObjectDataReferenceAt(object obj, IntPtr offset) - { - var rawObj = Unsafe.As(obj)!; - ref byte r0 = ref rawObj.Data; - ref byte r1 = ref Unsafe.AddByteOffset(ref r0, offset); - ref T r2 = ref Unsafe.As(ref r1); - - return ref r2; - } - - // Description adapted from CoreCLR, see: - // https://source.dot.net/#System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs,301. - // CLR objects are laid out in memory as follows: - // [ sync block || pMethodTable || raw data .. ] - // ^ ^ - // | \-- ref Unsafe.As(owner).Data - // \-- object - // The reference to RawObjectData.Data points to the first data byte in the - // target object, skipping over the sync block, method table and string length. - // Even though the description above links to the CoreCLR source, this approach - // can actually work on any .NET runtime, as it doesn't rely on a specific memory - // layout. Even if some 3rd party .NET runtime had some additional fields in the - // object header, before the field being referenced, the returned offset would still - // be valid when used on instances of that particular type, as it's only being - // used as a relative offset from the location pointed by the object reference. - [StructLayout(LayoutKind.Explicit)] - private sealed class RawObjectData - { - [FieldOffset(0)] -#pragma warning disable SA1401 // Fields should be private - public byte Data; -#pragma warning restore SA1401 - } - - /// - /// Tries to get a boxed value from an input instance. - /// - /// The type of value to try to unbox. - /// The input instance to check. - /// The resulting value, if was in fact a boxed value. - /// if a value was retrieved correctly, otherwise. - /// - /// This extension behaves just like the following method: - /// - /// public static bool TryUnbox<T>(object obj, out T value) - /// { - /// if (obj is T) - /// { - /// value = (T)obj; - /// - /// return true; - /// } - /// - /// value = default; - /// - /// return false; - /// } - /// - /// But in a more efficient way, and with the ability to also assign the unboxed value - /// directly on an existing T variable, which is not possible with the code above. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryUnbox(this object obj, out T value) - where T : struct - { - if (obj.GetType() == typeof(T)) - { - value = Unsafe.Unbox(obj); - - return true; - } - - value = default; - - return false; - } - - /// - /// Unboxes a value from an input instance. - /// - /// The type of value to unbox. - /// The input instance, representing a boxed value. - /// The value boxed in . - /// Thrown when is not of type . - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ref T DangerousUnbox(object obj) - where T : struct - { - return ref Unsafe.Unbox(obj); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs deleted file mode 100644 index 676a2b39dbd..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs +++ /dev/null @@ -1,249 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to work with parallel code in a highly optimized manner. - /// - public static partial class ParallelHelper - { -#if NETSTANDARD2_1_OR_GREATER - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The iteration range. - /// None of the bounds of can start from an end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For(Range range) - where TAction : struct, IAction - { - For(range, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The iteration range. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - /// None of the bounds of can start from an end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For(Range range, int minimumActionsPerThread) - where TAction : struct, IAction - { - For(range, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The iteration range. - /// The instance representing the action to invoke. - /// None of the bounds of can start from an end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For(Range range, in TAction action) - where TAction : struct, IAction - { - For(range, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The iteration range. - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - /// None of the bounds of can start from an end. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For(Range range, in TAction action, int minimumActionsPerThread) - where TAction : struct, IAction - { - if (range.Start.IsFromEnd || range.End.IsFromEnd) - { - ThrowArgumentExceptionForRangeIndexFromEnd(nameof(range)); - } - - int - start = range.Start.Value, - end = range.End.Value; - - For(start, end, action, minimumActionsPerThread); - } -#endif - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The starting iteration index. - /// The final iteration index (exclusive). - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For(int start, int end) - where TAction : struct, IAction - { - For(start, end, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The starting iteration index. - /// The final iteration index (exclusive). - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For(int start, int end, int minimumActionsPerThread) - where TAction : struct, IAction - { - For(start, end, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The starting iteration index. - /// The final iteration index (exclusive). - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For(int start, int end, in TAction action) - where TAction : struct, IAction - { - For(start, end, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each iteration index. - /// The starting iteration index. - /// The final iteration index (exclusive). - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - public static void For(int start, int end, in TAction action, int minimumActionsPerThread) - where TAction : struct, IAction - { - if (minimumActionsPerThread <= 0) - { - ThrowArgumentOutOfRangeExceptionForInvalidMinimumActionsPerThread(); - } - - if (start > end) - { - ThrowArgumentOutOfRangeExceptionForStartGreaterThanEnd(); - } - - if (start == end) - { - return; - } - - int - count = Math.Abs(start - end), - maxBatches = 1 + ((count - 1) / minimumActionsPerThread), - cores = Environment.ProcessorCount, - numBatches = Math.Min(maxBatches, cores); - - // Skip the parallel invocation when a single batch is needed - if (numBatches == 1) - { - for (int i = start; i < end; i++) - { - Unsafe.AsRef(action).Invoke(i); - } - - return; - } - - int batchSize = 1 + ((count - 1) / numBatches); - - var actionInvoker = new ActionInvoker(start, end, batchSize, action); - - // Run the batched operations in parallel - Parallel.For( - 0, - numBatches, - new ParallelOptions { MaxDegreeOfParallelism = numBatches }, - actionInvoker.Invoke); - } - - // Wrapping struct acting as explicit closure to execute the processing batches - private readonly struct ActionInvoker - where TAction : struct, IAction - { - private readonly int start; - private readonly int end; - private readonly int batchSize; - private readonly TAction action; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ActionInvoker( - int start, - int end, - int batchSize, - in TAction action) - { - this.start = start; - this.end = end; - this.batchSize = batchSize; - this.action = action; - } - - /// - /// Processes the batch of actions at a specified index - /// - /// The index of the batch to process - public void Invoke(int i) - { - int - offset = i * this.batchSize, - low = this.start + offset, - high = low + this.batchSize, - stop = Math.Min(high, this.end); - - for (int j = low; j < stop; j++) - { - Unsafe.AsRef(this.action).Invoke(j); - } - } - } - } - - /// - /// A contract for actions being executed with an input index. - /// - /// If the method is small enough, it is highly recommended to mark it with . - public interface IAction - { - /// - /// Executes the action associated with a specific index. - /// - /// The current index for the action to execute. - void Invoke(int i); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs deleted file mode 100644 index ae776f1566d..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs +++ /dev/null @@ -1,347 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Drawing; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to work with parallel code in a highly optimized manner. - /// - public static partial class ParallelHelper - { -#if NETSTANDARD2_1_OR_GREATER - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the iteration range for the outer loop. - /// The value indicating the iteration range for the inner loop. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Range i, Range j) - where TAction : struct, IAction2D - { - For2D(i, j, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the iteration range for the outer loop. - /// The value indicating the iteration range for the inner loop. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Range i, Range j, int minimumActionsPerThread) - where TAction : struct, IAction2D - { - For2D(i, j, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the iteration range for the outer loop. - /// The value indicating the iteration range for the inner loop. - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Range i, Range j, in TAction action) - where TAction : struct, IAction2D - { - For2D(i, j, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the iteration range for the outer loop. - /// The value indicating the iteration range for the inner loop. - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Range i, Range j, in TAction action, int minimumActionsPerThread) - where TAction : struct, IAction2D - { - if (i.Start.IsFromEnd || i.End.IsFromEnd) - { - ThrowArgumentExceptionForRangeIndexFromEnd(nameof(i)); - } - - if (j.Start.IsFromEnd || j.End.IsFromEnd) - { - ThrowArgumentExceptionForRangeIndexFromEnd(nameof(j)); - } - - int - top = i.Start.Value, - bottom = i.End.Value, - left = j.Start.Value, - right = j.End.Value; - - For2D(top, bottom, left, right, action, minimumActionsPerThread); - } -#endif - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the 2D iteration area to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Rectangle area) - where TAction : struct, IAction2D - { - For2D(area.Top, area.Bottom, area.Left, area.Right, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the 2D iteration area to use. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Rectangle area, int minimumActionsPerThread) - where TAction : struct, IAction2D - { - For2D(area.Top, area.Bottom, area.Left, area.Right, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the 2D iteration area to use. - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Rectangle area, in TAction action) - where TAction : struct, IAction2D - { - For2D(area.Top, area.Bottom, area.Left, area.Right, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The value indicating the 2D iteration area to use. - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(Rectangle area, in TAction action, int minimumActionsPerThread) - where TAction : struct, IAction2D - { - For2D(area.Top, area.Bottom, area.Left, area.Right, action, minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The starting iteration value for the outer loop. - /// The final iteration value for the outer loop (exclusive). - /// The starting iteration value for the inner loop. - /// The final iteration value for the inner loop (exclusive). - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(int top, int bottom, int left, int right) - where TAction : struct, IAction2D - { - For2D(top, bottom, left, right, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The starting iteration value for the outer loop. - /// The final iteration value for the outer loop (exclusive). - /// The starting iteration value for the inner loop. - /// The final iteration value for the inner loop (exclusive). - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(int top, int bottom, int left, int right, int minimumActionsPerThread) - where TAction : struct, IAction2D - { - For2D(top, bottom, left, right, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The starting iteration value for the outer loop. - /// The final iteration value for the outer loop (exclusive). - /// The starting iteration value for the inner loop. - /// The final iteration value for the inner loop (exclusive). - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void For2D(int top, int bottom, int left, int right, in TAction action) - where TAction : struct, IAction2D - { - For2D(top, bottom, left, right, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop. - /// - /// The type of action (implementing ) to invoke for each pair of iteration indices. - /// The starting iteration value for the outer loop. - /// The final iteration value for the outer loop (exclusive). - /// The starting iteration value for the inner loop. - /// The final iteration value for the inner loop (exclusive). - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - public static void For2D(int top, int bottom, int left, int right, in TAction action, int minimumActionsPerThread) - where TAction : struct, IAction2D - { - if (minimumActionsPerThread <= 0) - { - ThrowArgumentOutOfRangeExceptionForInvalidMinimumActionsPerThread(); - } - - if (top > bottom) - { - ThrowArgumentOutOfRangeExceptionForTopGreaterThanBottom(); - } - - if (left > right) - { - ThrowArgumentOutOfRangeExceptionForLeftGreaterThanRight(); - } - - // If either side of the target area is empty, no iterations are performed - if (top == bottom || left == right) - { - return; - } - - int - height = Math.Abs(top - bottom), - width = Math.Abs(left - right), - count = height * width, - maxBatches = 1 + ((count - 1) / minimumActionsPerThread), - clipBatches = Math.Min(maxBatches, height), - cores = Environment.ProcessorCount, - numBatches = Math.Min(clipBatches, cores); - - // Skip the parallel invocation when a single batch is needed - if (numBatches == 1) - { - for (int y = top; y < bottom; y++) - { - for (int x = left; x < right; x++) - { - Unsafe.AsRef(action).Invoke(y, x); - } - } - - return; - } - - int batchHeight = 1 + ((height - 1) / numBatches); - - var actionInvoker = new Action2DInvoker(top, bottom, left, right, batchHeight, action); - - // Run the batched operations in parallel - Parallel.For( - 0, - numBatches, - new ParallelOptions { MaxDegreeOfParallelism = numBatches }, - actionInvoker.Invoke); - } - - // Wrapping struct acting as explicit closure to execute the processing batches - private readonly struct Action2DInvoker - where TAction : struct, IAction2D - { - private readonly int startY; - private readonly int endY; - private readonly int startX; - private readonly int endX; - private readonly int batchHeight; - private readonly TAction action; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Action2DInvoker( - int startY, - int endY, - int startX, - int endX, - int batchHeight, - in TAction action) - { - this.startY = startY; - this.endY = endY; - this.startX = startX; - this.endX = endX; - this.batchHeight = batchHeight; - this.action = action; - } - - /// - /// Processes the batch of actions at a specified index - /// - /// The index of the batch to process - public void Invoke(int i) - { - int - heightOffset = i * this.batchHeight, - lowY = this.startY + heightOffset, - highY = lowY + this.batchHeight, - stopY = Math.Min(highY, this.endY); - - for (int y = lowY; y < stopY; y++) - { - for (int x = this.startX; x < this.endX; x++) - { - Unsafe.AsRef(this.action).Invoke(y, x); - } - } - } - } - } - - /// - /// A contract for actions being executed with two input indices. - /// - /// If the method is small enough, it is highly recommended to mark it with . - public interface IAction2D - { - /// - /// Executes the action associated with two specified indices. - /// - /// The first index for the action to execute. - /// The second index for the action to execute. - void Invoke(int i, int j); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs deleted file mode 100644 index 224d1961e43..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to work with parallel code in a highly optimized manner. - /// - public static partial class ParallelHelper - { - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(ReadOnlyMemory memory) - where TAction : struct, IInAction - { - ForEach(memory, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(ReadOnlyMemory memory, int minimumActionsPerThread) - where TAction : struct, IInAction - { - ForEach(memory, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(ReadOnlyMemory memory, in TAction action) - where TAction : struct, IInAction - { - ForEach(memory, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - public static void ForEach(ReadOnlyMemory memory, in TAction action, int minimumActionsPerThread) - where TAction : struct, IInAction - { - if (minimumActionsPerThread <= 0) - { - ThrowArgumentOutOfRangeExceptionForInvalidMinimumActionsPerThread(); - } - - if (memory.IsEmpty) - { - return; - } - - int - maxBatches = 1 + ((memory.Length - 1) / minimumActionsPerThread), - cores = Environment.ProcessorCount, - numBatches = Math.Min(maxBatches, cores); - - // Skip the parallel invocation when a single batch is needed - if (numBatches == 1) - { - foreach (var item in memory.Span) - { - Unsafe.AsRef(action).Invoke(item); - } - - return; - } - - int batchSize = 1 + ((memory.Length - 1) / numBatches); - - var actionInvoker = new InActionInvoker(batchSize, memory, action); - - // Run the batched operations in parallel - Parallel.For( - 0, - numBatches, - new ParallelOptions { MaxDegreeOfParallelism = numBatches }, - actionInvoker.Invoke); - } - - // Wrapping struct acting as explicit closure to execute the processing batches - private readonly struct InActionInvoker - where TAction : struct, IInAction - { - private readonly int batchSize; - private readonly ReadOnlyMemory memory; - private readonly TAction action; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public InActionInvoker( - int batchSize, - ReadOnlyMemory memory, - in TAction action) - { - this.batchSize = batchSize; - this.memory = memory; - this.action = action; - } - - /// - /// Processes the batch of actions at a specified index - /// - /// The index of the batch to process - public void Invoke(int i) - { - int - low = i * this.batchSize, - high = low + this.batchSize, - end = Math.Min(high, this.memory.Length); - - ref TItem r0 = ref MemoryMarshal.GetReference(this.memory.Span); - ref TItem rStart = ref Unsafe.Add(ref r0, low); - ref TItem rEnd = ref Unsafe.Add(ref r0, end); - - while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) - { - Unsafe.AsRef(this.action).Invoke(in rStart); - - rStart = ref Unsafe.Add(ref rStart, 1); - } - } - } - } - - /// - /// A contract for actions being executed on items of a specific type, with readonly access. - /// - /// The type of items to process. - /// If the method is small enough, it is highly recommended to mark it with . - public interface IInAction - { - /// - /// Executes the action on a specified item. - /// - /// The current item to process. - void Invoke(in T item); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs deleted file mode 100644 index cce703b2d74..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs +++ /dev/null @@ -1,160 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to work with parallel code in a highly optimized manner. - /// - public static partial class ParallelHelper - { - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(ReadOnlyMemory2D memory) - where TAction : struct, IInAction - { - ForEach(memory, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(ReadOnlyMemory2D memory, int minimumActionsPerThread) - where TAction : struct, IInAction - { - ForEach(memory, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(ReadOnlyMemory2D memory, in TAction action) - where TAction : struct, IInAction - { - ForEach(memory, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - public static void ForEach(ReadOnlyMemory2D memory, in TAction action, int minimumActionsPerThread) - where TAction : struct, IInAction - { - if (minimumActionsPerThread <= 0) - { - ThrowArgumentOutOfRangeExceptionForInvalidMinimumActionsPerThread(); - } - - if (memory.IsEmpty) - { - return; - } - - nint - maxBatches = 1 + ((memory.Length - 1) / minimumActionsPerThread), - clipBatches = maxBatches <= memory.Height ? maxBatches : memory.Height; - int - cores = Environment.ProcessorCount, - numBatches = (int)(clipBatches <= cores ? clipBatches : cores), - batchHeight = 1 + ((memory.Height - 1) / numBatches); - - var actionInvoker = new InActionInvokerWithReadOnlyMemory2D(batchHeight, memory, action); - - // Skip the parallel invocation when possible - if (numBatches == 1) - { - actionInvoker.Invoke(0); - - return; - } - - // Run the batched operations in parallel - Parallel.For( - 0, - numBatches, - new ParallelOptions { MaxDegreeOfParallelism = numBatches }, - actionInvoker.Invoke); - } - - // Wrapping struct acting as explicit closure to execute the processing batches - private readonly struct InActionInvokerWithReadOnlyMemory2D - where TAction : struct, IInAction - { - private readonly int batchHeight; - private readonly ReadOnlyMemory2D memory; - private readonly TAction action; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public InActionInvokerWithReadOnlyMemory2D( - int batchHeight, - ReadOnlyMemory2D memory, - in TAction action) - { - this.batchHeight = batchHeight; - this.memory = memory; - this.action = action; - } - - /// - /// Processes the batch of actions at a specified index - /// - /// The index of the batch to process - public void Invoke(int i) - { - int lowY = i * this.batchHeight; - nint highY = lowY + this.batchHeight; - int - stopY = (int)(highY <= this.memory.Height ? highY : this.memory.Height), - width = this.memory.Width; - - ReadOnlySpan2D span = this.memory.Span; - - for (int y = lowY; y < stopY; y++) - { - ref TItem rStart = ref span.DangerousGetReferenceAt(y, 0); - ref TItem rEnd = ref Unsafe.Add(ref rStart, width); - - while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) - { - Unsafe.AsRef(this.action).Invoke(in rStart); - - rStart = ref Unsafe.Add(ref rStart, 1); - } - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs deleted file mode 100644 index c88f8874d06..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to work with parallel code in a highly optimized manner. - /// - public static partial class ParallelHelper - { - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(Memory memory) - where TAction : struct, IRefAction - { - ForEach(memory, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(Memory memory, int minimumActionsPerThread) - where TAction : struct, IRefAction - { - ForEach(memory, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(Memory memory, in TAction action) - where TAction : struct, IRefAction - { - ForEach(memory, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - public static void ForEach(Memory memory, in TAction action, int minimumActionsPerThread) - where TAction : struct, IRefAction - { - if (minimumActionsPerThread <= 0) - { - ThrowArgumentOutOfRangeExceptionForInvalidMinimumActionsPerThread(); - } - - if (memory.IsEmpty) - { - return; - } - - int - maxBatches = 1 + ((memory.Length - 1) / minimumActionsPerThread), - cores = Environment.ProcessorCount, - numBatches = Math.Min(maxBatches, cores); - - // Skip the parallel invocation when a single batch is needed - if (numBatches == 1) - { - foreach (ref var item in memory.Span) - { - Unsafe.AsRef(action).Invoke(ref item); - } - - return; - } - - int batchSize = 1 + ((memory.Length - 1) / numBatches); - - var actionInvoker = new RefActionInvoker(batchSize, memory, action); - - // Run the batched operations in parallel - Parallel.For( - 0, - numBatches, - new ParallelOptions { MaxDegreeOfParallelism = numBatches }, - actionInvoker.Invoke); - } - - // Wrapping struct acting as explicit closure to execute the processing batches - private readonly struct RefActionInvoker - where TAction : struct, IRefAction - { - private readonly int batchSize; - private readonly ReadOnlyMemory memory; - private readonly TAction action; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RefActionInvoker( - int batchSize, - ReadOnlyMemory memory, - in TAction action) - { - this.batchSize = batchSize; - this.memory = memory; - this.action = action; - } - - /// - /// Processes the batch of actions at a specified index - /// - /// The index of the batch to process - public void Invoke(int i) - { - int - low = i * this.batchSize, - high = low + this.batchSize, - end = Math.Min(high, this.memory.Length); - - ref TItem r0 = ref MemoryMarshal.GetReference(this.memory.Span); - ref TItem rStart = ref Unsafe.Add(ref r0, low); - ref TItem rEnd = ref Unsafe.Add(ref r0, end); - - while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) - { - Unsafe.AsRef(this.action).Invoke(ref rStart); - - rStart = ref Unsafe.Add(ref rStart, 1); - } - } - } - } - - /// - /// A contract for actions being executed on items of a specific type, with side effect. - /// - /// The type of items to process. - /// If the method is small enough, it is highly recommended to mark it with . - public interface IRefAction - { - /// - /// Executes the action on a specified item. - /// - /// The current item to process. - void Invoke(ref T item); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs deleted file mode 100644 index 0d7f9b2ff34..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs +++ /dev/null @@ -1,167 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to work with parallel code in a highly optimized manner. - /// - public static partial class ParallelHelper - { - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(Memory2D memory) - where TAction : struct, IRefAction - { - ForEach(memory, default(TAction), 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(Memory2D memory, int minimumActionsPerThread) - where TAction : struct, IRefAction - { - ForEach(memory, default(TAction), minimumActionsPerThread); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ForEach(Memory2D memory, in TAction action) - where TAction : struct, IRefAction - { - ForEach(memory, action, 1); - } - - /// - /// Executes a specified action in an optimized parallel loop over the input data. - /// - /// The type of items to iterate over. - /// The type of action (implementing of ) to invoke over each item. - /// The input representing the data to process. - /// The instance representing the action to invoke. - /// - /// The minimum number of actions to run per individual thread. Set to 1 if all invocations - /// should be parallelized, or to a greater number if each individual invocation is fast - /// enough that it is more efficient to set a lower bound per each running thread. - /// - public static void ForEach(Memory2D memory, in TAction action, int minimumActionsPerThread) - where TAction : struct, IRefAction - { - if (minimumActionsPerThread <= 0) - { - ThrowArgumentOutOfRangeExceptionForInvalidMinimumActionsPerThread(); - } - - if (memory.IsEmpty) - { - return; - } - - // The underlying data for a Memory2D instance is bound to int.MaxValue in both - // axes, but its total size can exceed this value. Because of this, we calculate - // the target chunks as nint to avoid overflows, and switch back to int values - // for the rest of the setup, since the number of batches is bound to the number - // of CPU cores (which is an int), and the height of each batch is necessarily - // smaller than or equal than int.MaxValue, as it can't be greater than the - // number of total batches, which again is capped at the number of CPU cores. - nint - maxBatches = 1 + ((memory.Length - 1) / minimumActionsPerThread), - clipBatches = maxBatches <= memory.Height ? maxBatches : memory.Height; - int - cores = Environment.ProcessorCount, - numBatches = (int)(clipBatches <= cores ? clipBatches : cores), - batchHeight = 1 + ((memory.Height - 1) / numBatches); - - var actionInvoker = new RefActionInvokerWithReadOnlyMemory2D(batchHeight, memory, action); - - // Skip the parallel invocation when possible - if (numBatches == 1) - { - actionInvoker.Invoke(0); - - return; - } - - // Run the batched operations in parallel - Parallel.For( - 0, - numBatches, - new ParallelOptions { MaxDegreeOfParallelism = numBatches }, - actionInvoker.Invoke); - } - - // Wrapping struct acting as explicit closure to execute the processing batches - private readonly struct RefActionInvokerWithReadOnlyMemory2D - where TAction : struct, IRefAction - { - private readonly int batchHeight; - private readonly Memory2D memory; - private readonly TAction action; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RefActionInvokerWithReadOnlyMemory2D( - int batchHeight, - Memory2D memory, - in TAction action) - { - this.batchHeight = batchHeight; - this.memory = memory; - this.action = action; - } - - /// - /// Processes the batch of actions at a specified index - /// - /// The index of the batch to process - public void Invoke(int i) - { - int lowY = i * this.batchHeight; - nint highY = lowY + this.batchHeight; - int - stopY = (int)(highY <= this.memory.Height ? highY : this.memory.Height), - width = this.memory.Width; - - ReadOnlySpan2D span = this.memory.Span; - - for (int y = lowY; y < stopY; y++) - { - ref TItem rStart = ref span.DangerousGetReferenceAt(y, 0); - ref TItem rEnd = ref Unsafe.Add(ref rStart, width); - - while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) - { - Unsafe.AsRef(this.action).Invoke(ref rStart); - - rStart = ref Unsafe.Add(ref rStart, 1); - } - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ThrowExceptions.cs b/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ThrowExceptions.cs deleted file mode 100644 index 5d9941d5d49..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Helpers/ParallelHelper.ThrowExceptions.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Toolkit.HighPerformance.Helpers -{ - /// - /// Helpers to work with parallel code in a highly optimized manner. - /// - public static partial class ParallelHelper - { - /// - /// Throws an when an invalid parameter is specified for the minimum actions per thread. - /// - private static void ThrowArgumentOutOfRangeExceptionForInvalidMinimumActionsPerThread() - { - // Having the argument name here manually typed is - // not ideal, but this way we save passing that string as - // a parameter, since it's always the same anyway. - // Same goes for the other helper methods below. - throw new ArgumentOutOfRangeException( - "minimumActionsPerThread", - "Each thread needs to perform at least one action"); - } - - /// - /// Throws an when an invalid start parameter is specified for 1D loops. - /// - private static void ThrowArgumentOutOfRangeExceptionForStartGreaterThanEnd() - { - throw new ArgumentOutOfRangeException("start", "The start parameter must be less than or equal to end"); - } - - /// - /// Throws an when a range has an index starting from an end. - /// - private static void ThrowArgumentExceptionForRangeIndexFromEnd(string name) - { - throw new ArgumentException("The bounds of the range can't start from an end", name); - } - - /// - /// Throws an when an invalid top parameter is specified for 2D loops. - /// - private static void ThrowArgumentOutOfRangeExceptionForTopGreaterThanBottom() - { - throw new ArgumentOutOfRangeException("top", "The top parameter must be less than or equal to bottom"); - } - - /// - /// Throws an when an invalid left parameter is specified for 2D loops. - /// - private static void ThrowArgumentOutOfRangeExceptionForLeftGreaterThanRight() - { - throw new ArgumentOutOfRangeException("left", "The left parameter must be less than or equal to right"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Internals/OverflowHelper.cs b/Microsoft.Toolkit.HighPerformance/Memory/Internals/OverflowHelper.cs deleted file mode 100644 index b3385666d46..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/Internals/OverflowHelper.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using static System.Math; - -namespace Microsoft.Toolkit.HighPerformance.Memory.Internals -{ - /// - /// A helper to validate arithmetic operations for and . - /// - internal static class OverflowHelper - { - /// - /// Ensures that the input parameters will not exceed the maximum native int value when indexing. - /// - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - /// Throw when the inputs don't fit in the expected range. - /// The input parameters are assumed to always be positive. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EnsureIsInNativeIntRange(int height, int width, int pitch) - { - // As per the layout used in the Memory2D and Span2D types, we have the - // following memory representation with respect to height, width and pitch: - // - // _________width_________ ________... - // / \/ - // | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |_ - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- | | - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- | | - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- | |_height - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- |_| - // | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | - // | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | - // ...__pitch__/ - // - // The indexing logic works on nint values in unchecked mode, with no overflow checks, - // which means it relies on the maximum element index to always be within <= nint.MaxValue. - // To ensure no overflows will ever occur there, we need to ensure that no instance can be - // created with parameters that could cause an overflow in case any item was accessed, so we - // need to ensure no overflows occurs when calculating the index of the last item in each view. - // The logic below calculates that index with overflow checks, throwing if one is detected. - // Note that we're subtracting 1 to the height as we don't want to include the trailing pitch - // for the 2D memory area, and also 1 to the width as the index is 0-based, as usual. - // Additionally, we're also ensuring that the stride is never greater than int.MaxValue, for - // consistency with how ND arrays work (int.MaxValue as upper bound for each axis), and to - // allow for faster iteration in the RefEnumerable type, when traversing columns. - _ = checked(((nint)(width + pitch) * Max(unchecked(height - 1), 0)) + Max(unchecked(width - 1), 0)); - } - - /// - /// Ensures that the input parameters will not exceed when indexing. - /// - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - /// The area resulting from the given parameters. - /// Throw when the inputs don't fit in the expected range. - /// The input parameters are assumed to always be positive. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ComputeInt32Area(int height, int width, int pitch) - { - return checked(((width + pitch) * Max(unchecked(height - 1), 0)) + width); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Internals/ThrowHelper.cs b/Microsoft.Toolkit.HighPerformance/Memory/Internals/ThrowHelper.cs deleted file mode 100644 index 5cd58070915..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/Internals/ThrowHelper.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Toolkit.HighPerformance.Memory.Internals -{ - /// - /// A helper class to throw exceptions for memory types. - /// - internal static class ThrowHelper - { - /// - /// Throws an when using the * constructor with a managed type. - /// - public static void ThrowArgumentExceptionForManagedType() - { - throw new ArgumentException("Can't use a void* constructor when T is a managed type"); - } - - /// - /// Throws an when the target span is too short. - /// - public static void ThrowArgumentExceptionForDestinationTooShort() - { - throw new ArgumentException("The target span is too short to copy all the current items to"); - } - - /// - /// Throws an when the target span does not have the same shape as the source. - /// - public static void ThrowArgumentExceptionForDestinationWithNotSameShape() - { - throw new ArgumentException("The target span does not have the same shape as the source one"); - } - - /// - /// Throws an when using an array of an invalid type. - /// - public static void ThrowArrayTypeMismatchException() - { - throw new ArrayTypeMismatchException("The given array doesn't match the specified type T"); - } - - /// - /// Throws an when using an array of an invalid type. - /// - public static void ThrowArgumentExceptionForUnsupportedType() - { - throw new ArgumentException("The specified object type is not supported"); - } - - /// - /// Throws an when the a given coordinate is invalid. - /// - /// - /// Throwing is technically discouraged in the docs, but - /// we're doing that here for consistency with the official type(s) from the BCL. - /// - public static void ThrowIndexOutOfRangeException() - { - throw new IndexOutOfRangeException(); - } - - /// - /// Throws an when more than one parameter are invalid. - /// - public static void ThrowArgumentException() - { - throw new ArgumentException("One or more input parameters were invalid"); - } - - /// - /// Throws an when the "depth" parameter is invalid. - /// - public static void ThrowArgumentOutOfRangeExceptionForDepth() - { - throw new ArgumentOutOfRangeException("depth"); - } - - /// - /// Throws an when the "row" parameter is invalid. - /// - public static void ThrowArgumentOutOfRangeExceptionForRow() - { - throw new ArgumentOutOfRangeException("row"); - } - - /// - /// Throws an when the "column" parameter is invalid. - /// - public static void ThrowArgumentOutOfRangeExceptionForColumn() - { - throw new ArgumentOutOfRangeException("column"); - } - - /// - /// Throws an when the "offset" parameter is invalid. - /// - public static void ThrowArgumentOutOfRangeExceptionForOffset() - { - throw new ArgumentOutOfRangeException("offset"); - } - - /// - /// Throws an when the "height" parameter is invalid. - /// - public static void ThrowArgumentOutOfRangeExceptionForHeight() - { - throw new ArgumentOutOfRangeException("height"); - } - - /// - /// Throws an when the "width" parameter is invalid. - /// - public static void ThrowArgumentOutOfRangeExceptionForWidth() - { - throw new ArgumentOutOfRangeException("width"); - } - - /// - /// Throws an when the "pitch" parameter is invalid. - /// - public static void ThrowArgumentOutOfRangeExceptionForPitch() - { - throw new ArgumentOutOfRangeException("pitch"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Memory2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/Memory2D{T}.cs deleted file mode 100644 index be3c6e9f059..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/Memory2D{T}.cs +++ /dev/null @@ -1,905 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -#if SPAN_RUNTIME_SUPPORT -using Microsoft.Toolkit.HighPerformance.Buffers.Internals; -#endif -using Microsoft.Toolkit.HighPerformance.Helpers; -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -using Microsoft.Toolkit.HighPerformance.Memory.Views; -using static Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -#pragma warning disable CA2231 - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// represents a 2D region of arbitrary memory. It is to - /// what is to . For further details on how the internal layout - /// is structured, see the docs for . The type can wrap arrays - /// of any rank, provided that a valid series of parameters for the target memory area(s) are specified. - /// - /// The type of items in the current instance. - [DebuggerTypeProxy(typeof(MemoryDebugView2D<>))] - [DebuggerDisplay("{ToString(),raw}")] - public readonly struct Memory2D : IEquatable> - { - /// - /// The target instance, if present. - /// - private readonly object? instance; - - /// - /// The initial offset within . - /// - private readonly IntPtr offset; - - /// - /// The height of the specified 2D region. - /// - private readonly int height; - - /// - /// The width of the specified 2D region. - /// - private readonly int width; - - /// - /// The pitch of the specified 2D region. - /// - private readonly int pitch; - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - public Memory2D(T[] array, int height, int width) - : this(array, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - public Memory2D(T[] array, int offset, int height, int width, int pitch) - { - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - if ((uint)offset > (uint)array.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = array.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(offset)); - this.height = height; - this.width = width; - this.pitch = pitch; - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - /// - /// Thrown when doesn't match . - /// - public Memory2D(T[,]? array) - { - if (array is null) - { - this = default; - - return; - } - - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - this.instance = array; - this.offset = GetArray2DDataByteOffset(); - this.height = array.GetLength(0); - this.width = array.GetLength(1); - this.pitch = 0; - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for . - /// - public Memory2D(T[,]? array, int row, int column, int height, int width) - { - if (array is null) - { - if (row != 0 || column != 0 || height != 0 || width != 0) - { - ThrowHelper.ThrowArgumentException(); - } - - this = default; - - return; - } - - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - int - rows = array.GetLength(0), - columns = array.GetLength(1); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(row, column)); - this.height = height; - this.width = width; - this.pitch = columns - width; - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// - /// Thrown when doesn't match . - /// - /// Thrown when a parameter is invalid. - public Memory2D(T[,,] array, int depth) - { - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, 0, 0)); - this.height = array.GetLength(1); - this.width = array.GetLength(2); - this.pitch = 0; - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when doesn't match . - /// - /// Thrown when a parameter is invalid. - public Memory2D(T[,,] array, int depth, int row, int column, int height, int width) - { - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - - int - rows = array.GetLength(1), - columns = array.GetLength(2); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, row, column)); - this.height = height; - this.width = width; - this.pitch = columns - width; - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - public Memory2D(MemoryManager memoryManager, int height, int width) - : this(memoryManager, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - public Memory2D(MemoryManager memoryManager, int offset, int height, int width, int pitch) - { - int length = memoryManager.GetSpan().Length; - - if ((uint)offset > (uint)length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - if (width == 0 || height == 0) - { - this = default; - - return; - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - this.instance = memoryManager; - this.offset = (nint)(uint)offset; - this.height = height; - this.width = width; - this.pitch = pitch; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - internal Memory2D(Memory memory, int height, int width) - : this(memory, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - internal Memory2D(Memory memory, int offset, int height, int width, int pitch) - { - if ((uint)offset > (uint)memory.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - if (width == 0 || height == 0) - { - this = default; - - return; - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = memory.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - // Check if the Memory instance wraps a string. This is possible in case - // consumers do an unsafe cast for the entire Memory object, and while not - // really safe it is still supported in CoreCLR too, so we're following suit here. - if (typeof(T) == typeof(char) && - MemoryMarshal.TryGetString(Unsafe.As, Memory>(ref memory), out string? text, out int textStart, out _)) - { - ref char r0 = ref text.DangerousGetReferenceAt(textStart + offset); - - this.instance = text; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(text, ref r0); - } - else if (MemoryMarshal.TryGetArray(memory, out ArraySegment segment)) - { - // Check if the input Memory instance wraps an array we can access. - // This is fine, since Memory on its own doesn't control the lifetime - // of the underlying array anyway, and this Memory2D type would do the same. - // Using the array directly makes retrieving a Span2D faster down the line, - // as we no longer have to jump through the boxed Memory first anymore. - T[] array = segment.Array!; - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(segment.Offset + offset)); - } - else if (MemoryMarshal.TryGetMemoryManager>(memory, out var memoryManager, out int memoryManagerStart, out _)) - { - this.instance = memoryManager; - this.offset = (nint)(uint)(memoryManagerStart + offset); - } - else - { - ThrowHelper.ThrowArgumentExceptionForUnsupportedType(); - - this.instance = null; - this.offset = default; - } - - this.height = height; - this.width = width; - this.pitch = pitch; - } -#endif - - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The target instance. - /// The initial offset within . - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Memory2D(object instance, IntPtr offset, int height, int width, int pitch) - { - this.instance = instance; - this.offset = offset; - this.height = height; - this.width = width; - this.pitch = pitch; - } - - /// - /// Creates a new instance from an arbitrary object. - /// - /// The instance holding the data to map. - /// The target reference to point to (it must be within ). - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map. - /// A instance with the specified parameters. - /// The parameter is not validated, and it's responsibility of the caller to ensure it's valid. - /// - /// Thrown when one of the input parameters is out of range. - /// - [Pure] - public static Memory2D DangerousCreate(object instance, ref T value, int height, int width, int pitch) - { - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); - - IntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(instance, ref value); - - return new Memory2D(instance, offset, height, width, pitch); - } - - /// - /// Gets an empty instance. - /// - public static Memory2D Empty => default; - - /// - /// Gets a value indicating whether the current instance is empty. - /// - public bool IsEmpty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.height == 0 || this.width == 0; - } - - /// - /// Gets the length of the current instance. - /// - public nint Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (nint)(uint)this.height * (nint)(uint)this.width; - } - - /// - /// Gets the height of the underlying 2D memory area. - /// - public int Height - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.height; - } - - /// - /// Gets the width of the underlying 2D memory area. - /// - public int Width - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.width; - } - - /// - /// Gets a instance from the current memory. - /// - public Span2D Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (this.instance is not null) - { -#if SPAN_RUNTIME_SUPPORT - if (this.instance is MemoryManager memoryManager) - { - ref T r0 = ref memoryManager.GetSpan().DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, this.offset); - - return new Span2D(ref r1, this.height, this.width, this.pitch); - } - else - { - ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset); - - return new Span2D(ref r0, this.height, this.width, this.pitch); - } -#else - return new Span2D(this.instance, this.offset, this.height, this.width, this.pitch); -#endif - } - - return default; - } - } - -#if NETSTANDARD2_1_OR_GREATER - /// - /// Slices the current instance with the specified parameters. - /// - /// The target range of rows to select. - /// The target range of columns to select. - /// - /// Thrown when either or are invalid. - /// - /// A new instance representing a slice of the current one. - public Memory2D this[Range rows, Range columns] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - var (row, height) = rows.GetOffsetAndLength(this.height); - var (column, width) = columns.GetOffsetAndLength(this.width); - - return Slice(row, column, height, width); - } - } -#endif - - /// - /// Slices the current instance with the specified parameters. - /// - /// The target row to map within the current instance. - /// The target column to map within the current instance. - /// The height to map within the current instance. - /// The width to map within the current instance. - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for the current instance. - /// - /// A new instance representing a slice of the current one. - [Pure] - public Memory2D Slice(int row, int column, int height, int width) - { - if ((uint)row >= Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= this.width) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (Height - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (this.width - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - int - shift = ((this.width + this.pitch) * row) + column, - pitch = this.pitch + (this.width - width); - - IntPtr offset = this.offset + (shift * Unsafe.SizeOf()); - - return new Memory2D(this.instance!, offset, height, width, pitch); - } - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); - - /// - /// Copies the contents of this into a destination instance. - /// For this API to succeed, the target has to have the same shape as the current one. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Memory2D destination) => Span.CopyTo(destination.Span); - - /// - /// Attempts to copy the current instance to a destination . - /// For this API to succeed, the target has to have the same shape as the current one. - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Memory2D destination) => Span.TryCopyTo(destination.Span); - - /// - /// Creates a handle for the memory. - /// The GC will not move the memory until the returned - /// is disposed, enabling taking and using the memory's address. - /// - /// - /// An instance with nonprimitive (non-blittable) members cannot be pinned. - /// - /// A instance wrapping the pinned handle. - public unsafe MemoryHandle Pin() - { - if (this.instance is not null) - { - if (this.instance is MemoryManager memoryManager) - { - return memoryManager.Pin(); - } - - GCHandle handle = GCHandle.Alloc(this.instance, GCHandleType.Pinned); - - void* pointer = Unsafe.AsPointer(ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset)); - - return new MemoryHandle(pointer, handle); - } - - return default; - } - - /// - /// Tries to get a instance, if the underlying buffer is contiguous and small enough. - /// - /// The resulting , in case of success. - /// Whether or not was correctly assigned. - public bool TryGetMemory(out Memory memory) - { - if (this.pitch == 0 && - Length <= int.MaxValue) - { - // Empty Memory2D instance - if (this.instance is null) - { - memory = default; - } - else if (typeof(T) == typeof(char) && this.instance.GetType() == typeof(string)) - { - string text = Unsafe.As(this.instance)!; - int index = text.AsSpan().IndexOf(in ObjectMarshal.DangerousGetObjectDataReferenceAt(text, this.offset)); - ReadOnlyMemory temp = text.AsMemory(index, (int)Length); - - // The string type could still be present if a user ends up creating a - // Memory2D instance from a string using DangerousCreate. Similarly to - // how CoreCLR handles the equivalent case in Memory, here we just do - // the necessary steps to still retrieve a Memory instance correctly - // wrapping the target string. In this case, it is up to the caller - // to make sure not to ever actually write to the resulting Memory. - memory = MemoryMarshal.AsMemory(Unsafe.As, Memory>(ref temp)); - } - else if (this.instance is MemoryManager memoryManager) - { - // If the object is a MemoryManager, just slice it as needed - memory = memoryManager.Memory.Slice((int)(nint)this.offset, this.height * this.width); - } - else if (this.instance.GetType() == typeof(T[])) - { - // If it's a T[] array, also handle the initial offset - T[] array = Unsafe.As(this.instance)!; - int index = array.AsSpan().IndexOf(ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, this.offset)); - - memory = array.AsMemory(index, this.height * this.width); - } -#if SPAN_RUNTIME_SUPPORT - else if (this.instance.GetType() == typeof(T[,]) || - this.instance.GetType() == typeof(T[,,])) - { - // If the object is a 2D or 3D array, we can create a Memory from the RawObjectMemoryManager type. - // We just need to use the precomputed offset pointing to the first item in the current instance, - // and the current usable length. We don't need to retrieve the current index, as the manager just offsets. - memory = new RawObjectMemoryManager(this.instance, this.offset, this.height * this.width).Memory; - } -#endif - else - { - // Reuse a single failure path to reduce - // the number of returns in the method - goto Failure; - } - - return true; - } - - Failure: - - memory = default; - - return false; - } - - /// - /// Copies the contents of the current instance into a new 2D array. - /// - /// A 2D array containing the data in the current instance. - [Pure] - public T[,] ToArray() => Span.ToArray(); - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) - { - if (obj is Memory2D memory) - { - return Equals(memory); - } - - if (obj is ReadOnlyMemory2D readOnlyMemory) - { - return readOnlyMemory.Equals(this); - } - - return false; - } - - /// - public bool Equals(Memory2D other) - { - return - this.instance == other.instance && - this.offset == other.offset && - this.height == other.height && - this.width == other.width && - this.pitch == other.pitch; - } - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() - { - if (this.instance is not null) - { -#if !NETSTANDARD1_4 - return HashCode.Combine( - RuntimeHelpers.GetHashCode(this.instance), - this.offset, - this.height, - this.width, - this.pitch); -#else - Span values = stackalloc int[] - { - RuntimeHelpers.GetHashCode(this.instance), - this.offset.GetHashCode(), - this.height, - this.width, - this.pitch - }; - - return values.GetDjb2HashCode(); -#endif - } - - return 0; - } - - /// - public override string ToString() - { - return $"Microsoft.Toolkit.HighPerformance.Memory2D<{typeof(T)}>[{this.height}, {this.width}]"; - } - - /// - /// Defines an implicit conversion of an array to a - /// - public static implicit operator Memory2D(T[,]? array) => new(array); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs deleted file mode 100644 index 60d1c545f12..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlyMemory2D{T}.cs +++ /dev/null @@ -1,923 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -#if SPAN_RUNTIME_SUPPORT -using Microsoft.Toolkit.HighPerformance.Buffers.Internals; -#endif -using Microsoft.Toolkit.HighPerformance.Helpers; -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -using Microsoft.Toolkit.HighPerformance.Memory.Views; -using static Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; - -#pragma warning disable CA2231 - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// A readonly version of . - /// - /// The type of items in the current instance. - [DebuggerTypeProxy(typeof(MemoryDebugView2D<>))] - [DebuggerDisplay("{ToString(),raw}")] - public readonly struct ReadOnlyMemory2D : IEquatable> - { - /// - /// The target instance, if present. - /// - private readonly object? instance; - - /// - /// The initial offset within . - /// - private readonly IntPtr offset; - - /// - /// The height of the specified 2D region. - /// - private readonly int height; - - /// - /// The width of the specified 2D region. - /// - private readonly int width; - - /// - /// The pitch of the specified 2D region. - /// - private readonly int pitch; - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - public ReadOnlyMemory2D(string text, int height, int width) - : this(text, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - public ReadOnlyMemory2D(string text, int offset, int height, int width, int pitch) - { - if ((uint)offset > (uint)text.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = text.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - this.instance = text; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(text, ref text.DangerousGetReferenceAt(offset)); - this.height = height; - this.width = width; - this.pitch = pitch; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - public ReadOnlyMemory2D(T[] array, int height, int width) - : this(array, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - public ReadOnlyMemory2D(T[] array, int offset, int height, int width, int pitch) - { - if ((uint)offset > (uint)array.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = array.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(offset)); - this.height = height; - this.width = width; - this.pitch = pitch; - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - public ReadOnlyMemory2D(T[,]? array) - { - if (array is null) - { - this = default; - - return; - } - - this.instance = array; - this.offset = GetArray2DDataByteOffset(); - this.height = array.GetLength(0); - this.width = array.GetLength(1); - this.pitch = 0; - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for . - /// - public ReadOnlyMemory2D(T[,]? array, int row, int column, int height, int width) - { - if (array is null) - { - if (row != 0 || column != 0 || height != 0 || width != 0) - { - ThrowHelper.ThrowArgumentException(); - } - - this = default; - - return; - } - - int - rows = array.GetLength(0), - columns = array.GetLength(1); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(row, column)); - this.height = height; - this.width = width; - this.pitch = columns - width; - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// Thrown when a parameter is invalid. - public ReadOnlyMemory2D(T[,,] array, int depth) - { - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, 0, 0)); - this.height = array.GetLength(1); - this.width = array.GetLength(2); - this.pitch = 0; - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// Thrown when a parameter is invalid. - public ReadOnlyMemory2D(T[,,] array, int depth, int row, int column, int height, int width) - { - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - - int - rows = array.GetLength(1), - columns = array.GetLength(2); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, row, column)); - this.height = height; - this.width = width; - this.pitch = columns - width; - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - public ReadOnlyMemory2D(MemoryManager memoryManager, int height, int width) - : this(memoryManager, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - public ReadOnlyMemory2D(MemoryManager memoryManager, int offset, int height, int width, int pitch) - { - int length = memoryManager.GetSpan().Length; - - if ((uint)offset > (uint)length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - if (width == 0 || height == 0) - { - this = default; - - return; - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - this.instance = memoryManager; - this.offset = (nint)(uint)offset; - this.height = height; - this.width = width; - this.pitch = pitch; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - internal ReadOnlyMemory2D(ReadOnlyMemory memory, int height, int width) - : this(memory, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - internal ReadOnlyMemory2D(ReadOnlyMemory memory, int offset, int height, int width, int pitch) - { - if ((uint)offset > (uint)memory.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - if (width == 0 || height == 0) - { - this = default; - - return; - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = memory.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - // Check the supported underlying objects, like in Memory2D - if (typeof(T) == typeof(char) && - MemoryMarshal.TryGetString(Unsafe.As, ReadOnlyMemory>(ref memory), out string? text, out int textStart, out _)) - { - ref char r0 = ref text.DangerousGetReferenceAt(textStart + offset); - - this.instance = text; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(text, ref r0); - } - else if (MemoryMarshal.TryGetArray(memory, out ArraySegment segment)) - { - T[] array = segment.Array!; - - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(segment.Offset + offset)); - } - else if (MemoryMarshal.TryGetMemoryManager>(memory, out var memoryManager, out int memoryManagerStart, out _)) - { - this.instance = memoryManager; - this.offset = (nint)(uint)(memoryManagerStart + offset); - } - else - { - ThrowHelper.ThrowArgumentExceptionForUnsupportedType(); - - this.instance = null; - this.offset = default; - } - - this.height = height; - this.width = width; - this.pitch = pitch; - } -#endif - - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The target instance. - /// The initial offset within . - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ReadOnlyMemory2D(object instance, IntPtr offset, int height, int width, int pitch) - { - this.instance = instance; - this.offset = offset; - this.height = height; - this.width = width; - this.pitch = pitch; - } - - /// - /// Creates a new instance from an arbitrary object. - /// - /// The instance holding the data to map. - /// The target reference to point to (it must be within ). - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map. - /// A instance with the specified parameters. - /// The parameter is not validated, and it's responsibility of the caller to ensure it's valid. - /// - /// Thrown when one of the input parameters is out of range. - /// - [Pure] - public static ReadOnlyMemory2D DangerousCreate(object instance, ref T value, int height, int width, int pitch) - { - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); - - IntPtr offset = ObjectMarshal.DangerousGetObjectDataByteOffset(instance, ref value); - - return new ReadOnlyMemory2D(instance, offset, height, width, pitch); - } - - /// - /// Gets an empty instance. - /// - public static ReadOnlyMemory2D Empty => default; - - /// - /// Gets a value indicating whether the current instance is empty. - /// - public bool IsEmpty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.height == 0 || this.width == 0; - } - - /// - /// Gets the length of the current instance. - /// - public nint Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (nint)(uint)this.height * (nint)(uint)this.width; - } - - /// - /// Gets the height of the underlying 2D memory area. - /// - public int Height - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.height; - } - - /// - /// Gets the width of the underlying 2D memory area. - /// - public int Width - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.width; - } - - /// - /// Gets a instance from the current memory. - /// - public ReadOnlySpan2D Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (this.instance is not null) - { -#if SPAN_RUNTIME_SUPPORT - if (this.instance is MemoryManager memoryManager) - { - ref T r0 = ref memoryManager.GetSpan().DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, this.offset); - - return new ReadOnlySpan2D(in r1, this.height, this.width, this.pitch); - } - else - { - // This handles both arrays and strings - ref T r0 = ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset); - - return new ReadOnlySpan2D(in r0, this.height, this.width, this.pitch); - } -#else - return new ReadOnlySpan2D(this.instance, this.offset, this.height, this.width, this.pitch); -#endif - } - - return default; - } - } - -#if NETSTANDARD2_1_OR_GREATER - /// - /// Slices the current instance with the specified parameters. - /// - /// The target range of rows to select. - /// The target range of columns to select. - /// - /// Thrown when either or are invalid. - /// - /// A new instance representing a slice of the current one. - public ReadOnlyMemory2D this[Range rows, Range columns] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - var (row, height) = rows.GetOffsetAndLength(this.height); - var (column, width) = columns.GetOffsetAndLength(this.width); - - return Slice(row, column, height, width); - } - } -#endif - - /// - /// Slices the current instance with the specified parameters. - /// - /// The target row to map within the current instance. - /// The target column to map within the current instance. - /// The height to map within the current instance. - /// The width to map within the current instance. - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for the current instance. - /// - /// A new instance representing a slice of the current one. - [Pure] - public ReadOnlyMemory2D Slice(int row, int column, int height, int width) - { - if ((uint)row >= Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= this.width) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (Height - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (this.width - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - int - shift = ((this.width + this.pitch) * row) + column, - pitch = this.pitch + (this.width - width); - - IntPtr offset = this.offset + (shift * Unsafe.SizeOf()); - - return new ReadOnlyMemory2D(this.instance!, offset, height, width, pitch); - } - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Memory destination) => Span.CopyTo(destination.Span); - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Memory destination) => Span.TryCopyTo(destination.Span); - - /// - /// Copies the contents of this into a destination instance. - /// For this API to succeed, the target has to have the same shape as the current one. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Memory2D destination) => Span.CopyTo(destination.Span); - - /// - /// Attempts to copy the current instance to a destination . - /// For this API to succeed, the target has to have the same shape as the current one. - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Memory2D destination) => Span.TryCopyTo(destination.Span); - - /// - /// Creates a handle for the memory. - /// The GC will not move the memory until the returned - /// is disposed, enabling taking and using the memory's address. - /// - /// - /// An instance with nonprimitive (non-blittable) members cannot be pinned. - /// - /// A instance wrapping the pinned handle. - public unsafe MemoryHandle Pin() - { - if (this.instance is not null) - { - if (this.instance is MemoryManager memoryManager) - { - return memoryManager.Pin(); - } - - GCHandle handle = GCHandle.Alloc(this.instance, GCHandleType.Pinned); - - void* pointer = Unsafe.AsPointer(ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.instance, this.offset)); - - return new MemoryHandle(pointer, handle); - } - - return default; - } - - /// - /// Tries to get a instance, if the underlying buffer is contiguous and small enough. - /// - /// The resulting , in case of success. - /// Whether or not was correctly assigned. - public bool TryGetMemory(out ReadOnlyMemory memory) - { - if (this.pitch == 0 && - Length <= int.MaxValue) - { - // Empty Memory2D instance - if (this.instance is null) - { - memory = default; - } - else if (typeof(T) == typeof(char) && this.instance.GetType() == typeof(string)) - { - // Here we need to create a Memory from the wrapped string, and to do so we need to do an inverse - // lookup to find the initial index of the string with respect to the byte offset we're currently using, - // which refers to the raw string object data. This can include variable padding or other additional - // fields on different runtimes. The lookup operation is still O(1) and just computes the byte offset - // difference between the start of the Span (which directly wraps just the actual character data - // within the string), and the input reference, which we can get from the byte offset in use. The result - // is the character index which we can use to create the final Memory instance. - string text = Unsafe.As(this.instance)!; - int index = text.AsSpan().IndexOf(in ObjectMarshal.DangerousGetObjectDataReferenceAt(text, this.offset)); - ReadOnlyMemory temp = text.AsMemory(index, (int)Length); - - memory = Unsafe.As, ReadOnlyMemory>(ref temp); - } - else if (this.instance is MemoryManager memoryManager) - { - // If the object is a MemoryManager, just slice it as needed - memory = memoryManager.Memory.Slice((int)(nint)this.offset, this.height * this.width); - } - else if (this.instance.GetType() == typeof(T[])) - { - // If it's a T[] array, also handle the initial offset - T[] array = Unsafe.As(this.instance)!; - int index = array.AsSpan().IndexOf(ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, this.offset)); - - memory = array.AsMemory(index, this.height * this.width); - } -#if SPAN_RUNTIME_SUPPORT - else if (this.instance.GetType() == typeof(T[,]) || - this.instance.GetType() == typeof(T[,,])) - { - memory = new RawObjectMemoryManager(this.instance, this.offset, this.height * this.width).Memory; - } -#endif - else - { - // Reuse a single failure path to reduce - // the number of returns in the method - goto Failure; - } - - return true; - } - - Failure: - - memory = default; - - return false; - } - - /// - /// Copies the contents of the current instance into a new 2D array. - /// - /// A 2D array containing the data in the current instance. - [Pure] - public T[,] ToArray() => Span.ToArray(); - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object? obj) - { - if (obj is ReadOnlyMemory2D readOnlyMemory) - { - return Equals(readOnlyMemory); - } - - if (obj is Memory2D memory) - { - return Equals(memory); - } - - return false; - } - - /// - public bool Equals(ReadOnlyMemory2D other) - { - return - this.instance == other.instance && - this.offset == other.offset && - this.height == other.height && - this.width == other.width && - this.pitch == other.pitch; - } - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() - { - if (this.instance is not null) - { -#if !NETSTANDARD1_4 - return HashCode.Combine( - RuntimeHelpers.GetHashCode(this.instance), - this.offset, - this.height, - this.width, - this.pitch); -#else - Span values = stackalloc int[] - { - RuntimeHelpers.GetHashCode(this.instance), - this.offset.GetHashCode(), - this.height, - this.width, - this.pitch - }; - - return values.GetDjb2HashCode(); -#endif - } - - return 0; - } - - /// - public override string ToString() - { - return $"Microsoft.Toolkit.HighPerformance.ReadOnlyMemory2D<{typeof(T)}>[{this.height}, {this.width}]"; - } - - /// - /// Defines an implicit conversion of an array to a - /// - public static implicit operator ReadOnlyMemory2D(T[,]? array) => new(array); - - /// - /// Defines an implicit conversion of a to a - /// - public static implicit operator ReadOnlyMemory2D(Memory2D memory) => Unsafe.As, ReadOnlyMemory2D>(ref memory); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs deleted file mode 100644 index 8f6c50e2f43..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.Enumerator.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Enumerables; -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -#else -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; -#endif - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - public readonly ref partial struct ReadOnlySpan2D - { - /// - /// Gets an enumerable that traverses items in a specified row. - /// - /// The target row to enumerate within the current instance. - /// A with target items to enumerate. - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlyRefEnumerable GetRow(int row) - { - if ((uint)row >= Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - nint startIndex = (nint)(uint)this.stride * (nint)(uint)row; - ref T r0 = ref DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, startIndex); - -#if SPAN_RUNTIME_SUPPORT - return new ReadOnlyRefEnumerable(in r1, Width, 1); -#else - IntPtr offset = RuntimeHelpers.GetObjectDataOrReferenceByteOffset(this.instance, ref r1); - - return new ReadOnlyRefEnumerable(this.instance!, offset, this.width, 1); -#endif - } - - /// - /// Gets an enumerable that traverses items in a specified column. - /// - /// The target column to enumerate within the current instance. - /// A with target items to enumerate. - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlyRefEnumerable GetColumn(int column) - { - if ((uint)column >= Width) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - ref T r0 = ref DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, (nint)(uint)column); - -#if SPAN_RUNTIME_SUPPORT - return new ReadOnlyRefEnumerable(in r1, Height, this.stride); -#else - IntPtr offset = RuntimeHelpers.GetObjectDataOrReferenceByteOffset(this.instance, ref r1); - - return new ReadOnlyRefEnumerable(this.instance!, offset, Height, this.stride); -#endif - } - - /// - /// Returns an enumerator for the current instance. - /// - /// - /// An enumerator that can be used to traverse the items in the current instance - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new(this); - - /// - /// Provides an enumerator for the elements of a instance. - /// - public ref struct Enumerator - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance pointing to the first item in the target memory area. - /// - /// Just like in , the length is the height of the 2D region. - private readonly ReadOnlySpan span; -#else - /// - /// The target instance, if present. - /// - private readonly object? instance; - - /// - /// The initial offset within . - /// - private readonly IntPtr offset; - - /// - /// The height of the specified 2D region. - /// - private readonly int height; -#endif - - /// - /// The width of the specified 2D region. - /// - private readonly int width; - - /// - /// The stride of the specified 2D region. - /// - private readonly int stride; - - /// - /// The current horizontal offset. - /// - private int x; - - /// - /// The current vertical offset. - /// - private int y; - - /// - /// Initializes a new instance of the struct. - /// - /// The target instance to enumerate. - internal Enumerator(ReadOnlySpan2D span) - { -#if SPAN_RUNTIME_SUPPORT - this.span = span.span; -#else - this.instance = span.instance; - this.offset = span.offset; - this.height = span.height; -#endif - this.width = span.width; - this.stride = span.stride; - this.x = -1; - this.y = 0; - } - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int x = this.x + 1; - - // Horizontal move, within range - if (x < this.width) - { - this.x = x; - - return true; - } - - // We reached the end of a row and there is at least - // another row available: wrap to a new line and continue. - this.x = 0; - -#if SPAN_RUNTIME_SUPPORT - return ++this.y < this.span.Length; -#else - return ++this.y < this.height; -#endif - } - - /// - /// Gets the duck-typed property. - /// - public readonly ref readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - nint index = ((nint)(uint)this.y * (nint)(uint)this.stride) + (nint)(uint)this.x; - - return ref Unsafe.Add(ref r0, index); - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs deleted file mode 100644 index 114b6d22a48..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/ReadOnlySpan2D{T}.cs +++ /dev/null @@ -1,1035 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -#if !SPAN_RUNTIME_SUPPORT -using Microsoft.Toolkit.HighPerformance.Helpers; -#endif -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -using Microsoft.Toolkit.HighPerformance.Memory.Views; -#if !SPAN_RUNTIME_SUPPORT -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; -#endif - -#pragma warning disable CS0809, CA1065 - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// A readonly version of . - /// - /// The type of items in the current instance. - [DebuggerTypeProxy(typeof(MemoryDebugView2D<>))] - [DebuggerDisplay("{ToString(),raw}")] - public readonly ref partial struct ReadOnlySpan2D - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance pointing to the first item in the target memory area. - /// - private readonly ReadOnlySpan span; -#else - /// - /// The target instance, if present. - /// - private readonly object? instance; - - /// - /// The initial offset within . - /// - private readonly IntPtr offset; - - /// - /// The height of the specified 2D region. - /// - private readonly int height; -#endif - - /// - /// The width of the specified 2D region. - /// - private readonly int width; - - /// - /// The stride of the specified 2D region. - /// - private readonly int stride; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The reference to the first item to map. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlySpan2D(in T value, int height, int width, int pitch) - { - this.span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(value), height); - this.width = width; - this.stride = width + pitch; - } -#endif - - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The pointer to the first item to map. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - /// Thrown when one of the parameters are negative. - public unsafe ReadOnlySpan2D(void* pointer, int height, int width, int pitch) - { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - ThrowHelper.ThrowArgumentExceptionForManagedType(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); - -#if SPAN_RUNTIME_SUPPORT - this.span = new ReadOnlySpan(pointer, height); -#else - this.instance = null; - this.offset = (IntPtr)pointer; - this.height = height; -#endif - this.width = width; - this.stride = width + pitch; - } - -#if !SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The target instance. - /// The initial offset within the target instance. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlySpan2D(object? instance, IntPtr offset, int height, int width, int pitch) - { - this.instance = instance; - this.offset = offset; - this.height = height; - this.width = width; - this.stride = width + pitch; - } -#endif - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - public ReadOnlySpan2D(T[] array, int height, int width) - : this(array, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - public ReadOnlySpan2D(T[] array, int offset, int height, int width, int pitch) - { - if ((uint)offset > (uint)array.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - if (width == 0 || height == 0) - { - this = default; - - return; - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = array.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(offset), height); -#else - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(offset)); - this.height = height; -#endif - this.width = width; - this.stride = width + pitch; - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - public ReadOnlySpan2D(T[,]? array) - { - if (array is null) - { - this = default; - - return; - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReference(), array.GetLength(0)); -#else - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(0, 0)); - this.height = array.GetLength(0); -#endif - this.width = this.stride = array.GetLength(1); - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for . - /// - public ReadOnlySpan2D(T[,]? array, int row, int column, int height, int width) - { - if (array is null) - { - if (row != 0 || column != 0 || height != 0 || width != 0) - { - ThrowHelper.ThrowArgumentException(); - } - - this = default; - - return; - } - - int - rows = array.GetLength(0), - columns = array.GetLength(1); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(row, column), height); -#else - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(row, column)); - this.height = height; -#endif - this.width = width; - this.stride = columns; - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// Thrown when a parameter is invalid. - public ReadOnlySpan2D(T[,,] array, int depth) - { - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(depth, 0, 0), array.GetLength(1)); -#else - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, 0, 0)); - this.height = array.GetLength(1); -#endif - this.width = this.stride = array.GetLength(2); - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// Thrown when a parameter is invalid. - public ReadOnlySpan2D(T[,,] array, int depth, int row, int column, int height, int width) - { - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - - int - rows = array.GetLength(1), - columns = array.GetLength(2); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateReadOnlySpan(ref array.DangerousGetReferenceAt(depth, row, column), height); -#else - this.instance = array; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, row, column)); - this.height = height; -#endif - this.width = width; - this.stride = columns; - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - internal ReadOnlySpan2D(ReadOnlySpan span, int height, int width) - : this(span, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - internal ReadOnlySpan2D(ReadOnlySpan span, int offset, int height, int width, int pitch) - { - if ((uint)offset > (uint)span.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - if (width == 0 || height == 0) - { - this = default; - - return; - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = span.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - this.span = MemoryMarshal.CreateSpan(ref span.DangerousGetReferenceAt(offset), height); - this.width = width; - this.stride = width + pitch; - } - - /// - /// Creates a new instance of the struct with the specified parameters. - /// - /// The reference to the first item to map. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - /// A instance with the specified parameters. - /// Thrown when one of the parameters are negative. - [Pure] - public static ReadOnlySpan2D DangerousCreate(in T value, int height, int width, int pitch) - { - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); - - return new ReadOnlySpan2D(in value, height, width, pitch); - } -#endif - - /// - /// Gets an empty instance. - /// - public static ReadOnlySpan2D Empty => default; - - /// - /// Gets a value indicating whether the current instance is empty. - /// - public bool IsEmpty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Height == 0 || this.width == 0; - } - - /// - /// Gets the length of the current instance. - /// - public nint Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (nint)(uint)Height * (nint)(uint)this.width; - } - - /// - /// Gets the height of the underlying 2D memory area. - /// - public int Height - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return this.span.Length; -#else - return this.height; -#endif - } - } - - /// - /// Gets the width of the underlying 2D memory area. - /// - public int Width - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.width; - } - - /// - /// Gets the element at the specified zero-based indices. - /// - /// The target row to get the element from. - /// The target column to get the element from. - /// A reference to the element at the specified indices. - /// - /// Thrown when either or are invalid. - /// - public ref readonly T this[int row, int column] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if ((uint)row >= (uint)Height || - (uint)column >= (uint)Width) - { - ThrowHelper.ThrowIndexOutOfRangeException(); - } - - return ref DangerousGetReferenceAt(row, column); - } - } - -#if NETSTANDARD2_1_OR_GREATER - /// - /// Gets the element at the specified zero-based indices. - /// - /// The target row to get the element from. - /// The target column to get the element from. - /// A reference to the element at the specified indices. - /// - /// Thrown when either or are invalid. - /// - public ref readonly T this[Index row, Index column] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref this[row.GetOffset(Height), column.GetOffset(this.width)]; - } - - /// - /// Slices the current instance with the specified parameters. - /// - /// The target range of rows to select. - /// The target range of columns to select. - /// - /// Thrown when either or are invalid. - /// - /// A new instance representing a slice of the current one. - public ReadOnlySpan2D this[Range rows, Range columns] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - var (row, height) = rows.GetOffsetAndLength(Height); - var (column, width) = columns.GetOffsetAndLength(this.width); - - return Slice(row, column, height, width); - } - } -#endif - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Span destination) - { - if (IsEmpty) - { - return; - } - - if (TryGetSpan(out ReadOnlySpan span)) - { - span.CopyTo(destination); - } - else - { - if (Length > destination.Length) - { - ThrowHelper.ThrowArgumentExceptionForDestinationTooShort(); - } - - // Copy each row individually -#if SPAN_RUNTIME_SUPPORT - for (int i = 0, j = 0; i < Height; i++, j += this.width) - { - GetRowSpan(i).CopyTo(destination.Slice(j)); - } -#else - int height = Height; - nint width = (nint)(uint)this.width; - - ref T destinationRef = ref MemoryMarshal.GetReference(destination); - - for (int i = 0; i < height; i++) - { - ref T sourceStart = ref DangerousGetReferenceAt(i, 0); - ref T sourceEnd = ref Unsafe.Add(ref sourceStart, width); - - while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) - { - destinationRef = sourceStart; - - sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destinationRef = ref Unsafe.Add(ref destinationRef, 1); - } - } -#endif - } - } - - /// - /// Copies the contents of this into a destination instance. - /// For this API to succeed, the target has to have the same shape as the current one. - /// - /// The destination instance. - /// - /// Thrown when does not have the same shape as the source instance. - /// - public void CopyTo(Span2D destination) - { - if (destination.Height != Height || - destination.Width != Width) - { - ThrowHelper.ThrowArgumentExceptionForDestinationWithNotSameShape(); - } - - if (IsEmpty) - { - return; - } - - if (destination.TryGetSpan(out Span span)) - { - CopyTo(span); - } - else - { - // Copy each row individually -#if SPAN_RUNTIME_SUPPORT - for (int i = 0; i < Height; i++) - { - GetRowSpan(i).CopyTo(destination.GetRowSpan(i)); - } -#else - int height = Height; - nint width = (nint)(uint)this.width; - - for (int i = 0; i < height; i++) - { - ref T sourceStart = ref DangerousGetReferenceAt(i, 0); - ref T sourceEnd = ref Unsafe.Add(ref sourceStart, width); - ref T destinationRef = ref destination.DangerousGetReferenceAt(i, 0); - - while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) - { - destinationRef = sourceStart; - - sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destinationRef = ref Unsafe.Add(ref destinationRef, 1); - } - } -#endif - } - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Span destination) - { - if (destination.Length >= Length) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Span2D destination) - { - if (destination.Height == Height && - destination.Width == Width) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - /// Returns a reference to the 0th element of the instance. If the current - /// instance is empty, returns a reference. It can be used for pinning - /// and is required to support the use of span within a fixed statement. - /// - /// A reference to the 0th element, or a reference. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [EditorBrowsable(EditorBrowsableState.Never)] - public unsafe ref T GetPinnableReference() - { - ref T r0 = ref Unsafe.AsRef(null); - - if (Length != 0) - { -#if SPAN_RUNTIME_SUPPORT - r0 = ref MemoryMarshal.GetReference(this.span); -#else - r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - } - - return ref r0; - } - - /// - /// Returns a reference to the first element within the current instance, with no bounds check. - /// - /// A reference to the first element within the current instance. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetReference() - { -#if SPAN_RUNTIME_SUPPORT - return ref MemoryMarshal.GetReference(this.span); -#else - return ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - } - - /// - /// Returns a reference to a specified element within the current instance, with no bounds check. - /// - /// The target row to get the element from. - /// The target column to get the element from. - /// A reference to the element at the specified indices. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetReferenceAt(int i, int j) - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - nint index = ((nint)(uint)i * (nint)(uint)this.stride) + (nint)(uint)j; - - return ref Unsafe.Add(ref r0, index); - } - - /// - /// Slices the current instance with the specified parameters. - /// - /// The target row to map within the current instance. - /// The target column to map within the current instance. - /// The height to map within the current instance. - /// The width to map within the current instance. - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for the current instance. - /// - /// A new instance representing a slice of the current one. - [Pure] - public ReadOnlySpan2D Slice(int row, int column, int height, int width) - { - if ((uint)row >= Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= this.width) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (Height - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (this.width - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - nint shift = ((nint)(uint)this.stride * (nint)(uint)row) + (nint)(uint)column; - int pitch = this.stride - width; - -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref this.span.DangerousGetReferenceAt(shift); - - return new ReadOnlySpan2D(in r0, height, width, pitch); -#else - IntPtr offset = this.offset + (shift * (nint)(uint)Unsafe.SizeOf()); - - return new ReadOnlySpan2D(this.instance, offset, height, width, pitch); -#endif - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Gets a for a specified row. - /// - /// The index of the target row to retrieve. - /// Throw when is out of range. - /// The resulting row . - [Pure] - public ReadOnlySpan GetRowSpan(int row) - { - if ((uint)row >= (uint)Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - ref T r0 = ref DangerousGetReferenceAt(row, 0); - - return MemoryMarshal.CreateReadOnlySpan(ref r0, this.width); - } -#endif - - /// - /// Tries to get a instance, if the underlying buffer is contiguous and small enough. - /// - /// The resulting , in case of success. - /// Whether or not was correctly assigned. - public bool TryGetSpan(out ReadOnlySpan span) - { - // We can only create a Span if the buffer is contiguous - if (this.stride == this.width && - Length <= int.MaxValue) - { -#if SPAN_RUNTIME_SUPPORT - span = MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(this.span), (int)Length); - - return true; -#else - // An empty Span2D is still valid - if (IsEmpty) - { - span = default; - - return true; - } - - // Pinned ReadOnlySpan2D - if (this.instance is null) - { - unsafe - { - span = new Span((void*)this.offset, (int)Length); - } - - return true; - } - - // Without Span runtime support, we can only get a Span from a T[] instance - if (this.instance.GetType() == typeof(T[])) - { - T[] array = Unsafe.As(this.instance)!; - int index = array.AsSpan().IndexOf(ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, this.offset)); - - span = array.AsSpan(index, (int)Length); - - return true; - } -#endif - } - - span = default; - - return false; - } - - /// - /// Copies the contents of the current instance into a new 2D array. - /// - /// A 2D array containing the data in the current instance. - [Pure] - public T[,] ToArray() - { - T[,] array = new T[Height, this.width]; - -#if SPAN_RUNTIME_SUPPORT - CopyTo(array.AsSpan()); -#else - // Skip the initialization if the array is empty - if (Length > 0) - { - int height = Height; - nint width = (nint)(uint)this.width; - - ref T destinationRef = ref array.DangerousGetReference(); - - for (int i = 0; i < height; i++) - { - ref T sourceStart = ref DangerousGetReferenceAt(i, 0); - ref T sourceEnd = ref Unsafe.Add(ref sourceStart, width); - - while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) - { - destinationRef = sourceStart; - - sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destinationRef = ref Unsafe.Add(ref destinationRef, 1); - } - } - } -#endif - - return array; - } - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] - public override bool Equals(object? obj) - { - throw new NotSupportedException("Microsoft.Toolkit.HighPerformance.ReadOnlySpan2D.Equals(object) is not supported"); - } - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("GetHashCode() on Span will always throw an exception.")] - public override int GetHashCode() - { - throw new NotSupportedException("Microsoft.Toolkit.HighPerformance.ReadOnlySpan2D.GetHashCode() is not supported"); - } - - /// - public override string ToString() - { - return $"Microsoft.Toolkit.HighPerformance.ReadOnlySpan2D<{typeof(T)}>[{Height}, {this.width}]"; - } - - /// - /// Checks whether two instances are equal. - /// - /// The first instance to compare. - /// The second instance to compare. - /// Whether or not and are equal. - public static bool operator ==(ReadOnlySpan2D left, ReadOnlySpan2D right) - { - return -#if SPAN_RUNTIME_SUPPORT - left.span == right.span && -#else - ReferenceEquals(left.instance, right.instance) && - left.offset == right.offset && - left.height == right.height && -#endif - left.width == right.width && - left.stride == right.stride; - } - - /// - /// Checks whether two instances are not equal. - /// - /// The first instance to compare. - /// The second instance to compare. - /// Whether or not and are not equal. - public static bool operator !=(ReadOnlySpan2D left, ReadOnlySpan2D right) - { - return !(left == right); - } - - /// - /// Implicily converts a given 2D array into a instance. - /// - /// The input 2D array to convert. - public static implicit operator ReadOnlySpan2D(T[,]? array) => new(array); - - /// - /// Implicily converts a given into a instance. - /// - /// The input to convert. - public static implicit operator ReadOnlySpan2D(Span2D span) - { -#if SPAN_RUNTIME_SUPPORT - return new ReadOnlySpan2D(in span.DangerousGetReference(), span.Height, span.Width, span.Stride - span.Width); -#else - return new ReadOnlySpan2D(span.Instance!, span.Offset, span.Height, span.Width, span.Stride - span.Width); -#endif - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs b/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs deleted file mode 100644 index ba75985720e..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.Enumerator.cs +++ /dev/null @@ -1,201 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Enumerables; -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -#else -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; -#endif - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - public readonly ref partial struct Span2D - { - /// - /// Gets an enumerable that traverses items in a specified row. - /// - /// The target row to enumerate within the current instance. - /// A with target items to enumerate. - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RefEnumerable GetRow(int row) - { - if ((uint)row >= Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - nint startIndex = (nint)(uint)this.Stride * (nint)(uint)row; - ref T r0 = ref DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, startIndex); - -#if SPAN_RUNTIME_SUPPORT - return new RefEnumerable(ref r1, Width, 1); -#else - IntPtr offset = RuntimeHelpers.GetObjectDataOrReferenceByteOffset(this.Instance, ref r1); - - return new RefEnumerable(this.Instance, offset, this.width, 1); -#endif - } - - /// - /// Gets an enumerable that traverses items in a specified column. - /// - /// The target column to enumerate within the current instance. - /// A with target items to enumerate. - /// The returned value shouldn't be used directly: use this extension in a loop. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public RefEnumerable GetColumn(int column) - { - if ((uint)column >= Width) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - ref T r0 = ref DangerousGetReference(); - ref T r1 = ref Unsafe.Add(ref r0, (nint)(uint)column); - -#if SPAN_RUNTIME_SUPPORT - return new RefEnumerable(ref r1, Height, this.Stride); -#else - IntPtr offset = RuntimeHelpers.GetObjectDataOrReferenceByteOffset(this.Instance, ref r1); - - return new RefEnumerable(this.Instance, offset, Height, this.Stride); -#endif - } - - /// - /// Returns an enumerator for the current instance. - /// - /// - /// An enumerator that can be used to traverse the items in the current instance - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new(this); - - /// - /// Provides an enumerator for the elements of a instance. - /// - public ref struct Enumerator - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance pointing to the first item in the target memory area. - /// - /// Just like in , the length is the height of the 2D region. - private readonly Span span; -#else - /// - /// The target instance, if present. - /// - private readonly object? instance; - - /// - /// The initial offset within . - /// - private readonly IntPtr offset; - - /// - /// The height of the specified 2D region. - /// - private readonly int height; -#endif - - /// - /// The width of the specified 2D region. - /// - private readonly int width; - - /// - /// The stride of the specified 2D region. - /// - private readonly int stride; - - /// - /// The current horizontal offset. - /// - private int x; - - /// - /// The current vertical offset. - /// - private int y; - - /// - /// Initializes a new instance of the struct. - /// - /// The target instance to enumerate. - internal Enumerator(Span2D span) - { -#if SPAN_RUNTIME_SUPPORT - this.span = span.span; -#else - this.instance = span.Instance; - this.offset = span.Offset; - this.height = span.height; -#endif - this.width = span.width; - this.stride = span.Stride; - this.x = -1; - this.y = 0; - } - - /// - /// Implements the duck-typed method. - /// - /// whether a new element is available, otherwise - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int x = this.x + 1; - - // Horizontal move, within range - if (x < this.width) - { - this.x = x; - - return true; - } - - // We reached the end of a row and there is at least - // another row available: wrap to a new line and continue. - this.x = 0; - -#if SPAN_RUNTIME_SUPPORT - return ++this.y < this.span.Length; -#else - return ++this.y < this.height; -#endif - } - - /// - /// Gets the duck-typed property. - /// - public readonly ref T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.instance, this.offset); -#endif - nint index = ((nint)(uint)this.y * (nint)(uint)this.stride) + (nint)(uint)this.x; - - return ref Unsafe.Add(ref r0, index); - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs deleted file mode 100644 index b2a426105a8..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/Span2D{T}.cs +++ /dev/null @@ -1,1178 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -#if !SPAN_RUNTIME_SUPPORT -using Microsoft.Toolkit.HighPerformance.Helpers; -#endif -using Microsoft.Toolkit.HighPerformance.Memory.Internals; -using Microsoft.Toolkit.HighPerformance.Memory.Views; -#if !SPAN_RUNTIME_SUPPORT -using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers; -#endif - -#pragma warning disable CS0809, CA1065 - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// represents a 2D region of arbitrary memory. Like the type, - /// it can point to either managed or native memory, or to memory allocated on the stack. It is type- and memory-safe. - /// One key difference with and arrays is that the underlying buffer for a - /// instance might not be contiguous in memory: this is supported to enable mapping arbitrary 2D regions even if they - /// require padding between boundaries of sequential rows. All this logic is handled internally by the - /// type and it is transparent to the user, but note that working over discontiguous buffers has a performance impact. - /// - /// The type of items in the current instance. - [DebuggerTypeProxy(typeof(MemoryDebugView2D<>))] - [DebuggerDisplay("{ToString(),raw}")] - public readonly ref partial struct Span2D - { - // Let's consider a representation of a discontiguous 2D memory - // region within an existing array. The data is represented in - // row-major order as usual, and the 'XX' grid cells represent - // locations that are mapped by a given Span2D instance: - // - // _____________________stride_____... - // reference__ /________width_________ ________... - // \/ \/ - // | -- | -- | |- | -- | -- | -- | -- | -- | -- | -- |_ - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- | | - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- | | - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- | |_height - // | -- | -- | XX | XX | XX | XX | XX | XX | -- | -- |_| - // | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | - // | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | - // ...__pitch__/ - // ...________/ - // - // The pitch is used to calculate the offset between each - // discontiguous row, so that any arbitrary memory locations - // can be used to internally represent a 2D span. This gives - // users much more flexibility when creating spans from data. -#if SPAN_RUNTIME_SUPPORT - /// - /// The instance pointing to the first item in the target memory area. - /// - /// - /// The field maps to the height of the 2D region. - /// This is done to save 4 bytes in the layout of the type. - /// - private readonly Span span; -#else - /// - /// The target instance, if present. - /// - internal readonly object? Instance; - - /// - /// The initial offset within . - /// - internal readonly IntPtr Offset; - - /// - /// The height of the specified 2D region. - /// - private readonly int height; -#endif - - /// - /// The width of the specified 2D region. - /// - private readonly int width; - - /// - /// The stride of the specified 2D region. - /// - /// - /// This combines both the width and pitch in a single value so that the indexing - /// logic can be simplified (no need to recompute the sum every time) and be faster. - /// - internal readonly int Stride; - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The reference to the first item to map. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Span2D(ref T value, int height, int width, int pitch) - { - this.span = MemoryMarshal.CreateSpan(ref value, height); - this.width = width; - this.Stride = width + pitch; - } -#endif - - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The pointer to the first item to map. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - /// Thrown when one of the parameters are negative. - public unsafe Span2D(void* pointer, int height, int width, int pitch) - { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - ThrowHelper.ThrowArgumentExceptionForManagedType(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); - -#if SPAN_RUNTIME_SUPPORT - this.span = new Span(pointer, height); -#else - this.Instance = null; - this.Offset = (IntPtr)pointer; - this.height = height; -#endif - this.width = width; - this.Stride = width + pitch; - } - -#if !SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct with the specified parameters. - /// - /// The target instance. - /// The initial offset within the target instance. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Span2D(object? instance, IntPtr offset, int height, int width, int pitch) - { - this.Instance = instance; - this.Offset = offset; - this.height = height; - this.width = width; - this.Stride = width + pitch; - } -#endif - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - public Span2D(T[] array, int height, int width) - : this(array, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target array to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - public Span2D(T[] array, int offset, int height, int width, int pitch) - { - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - if ((uint)offset > (uint)array.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = array.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(offset), height); -#else - this.Instance = array; - this.Offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(offset)); - this.height = height; -#endif - this.width = width; - this.Stride = width + pitch; - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - /// - /// Thrown when doesn't match . - /// - public Span2D(T[,]? array) - { - if (array is null) - { - this = default; - - return; - } - - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReference(), array.GetLength(0)); -#else - this.Instance = array; - this.Offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(0, 0)); - this.height = array.GetLength(0); -#endif - this.width = this.Stride = array.GetLength(1); - } - - /// - /// Initializes a new instance of the struct wrapping a 2D array. - /// - /// The given 2D array to wrap. - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when doesn't match . - /// - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for . - /// - public Span2D(T[,]? array, int row, int column, int height, int width) - { - if (array is null) - { - if (row != 0 || column != 0 || height != 0 || width != 0) - { - ThrowHelper.ThrowArgumentException(); - } - - this = default; - - return; - } - - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - int - rows = array.GetLength(0), - columns = array.GetLength(1); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(row, column), height); -#else - this.Instance = array; - this.Offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(row, column)); - this.height = height; -#endif - this.width = width; - this.Stride = columns; - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// - /// Thrown when doesn't match . - /// - /// Thrown when a parameter is invalid. - public Span2D(T[,,] array, int depth) - { - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(depth, 0, 0), array.GetLength(1)); -#else - this.Instance = array; - this.Offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, 0, 0)); - this.height = array.GetLength(1); -#endif - this.width = this.Stride = array.GetLength(2); - } - - /// - /// Initializes a new instance of the struct wrapping a layer in a 3D array. - /// - /// The given 3D array to wrap. - /// The target layer to map within . - /// The target row to map within . - /// The target column to map within . - /// The height to map within . - /// The width to map within . - /// - /// Thrown when doesn't match . - /// - /// Thrown when a parameter is invalid. - public Span2D(T[,,] array, int depth, int row, int column, int height, int width) - { - if (array.IsCovariant()) - { - ThrowHelper.ThrowArrayTypeMismatchException(); - } - - if ((uint)depth >= (uint)array.GetLength(0)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForDepth(); - } - - int - rows = array.GetLength(1), - columns = array.GetLength(2); - - if ((uint)row >= (uint)rows) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= (uint)columns) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (uint)(rows - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (uint)(columns - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - -#if SPAN_RUNTIME_SUPPORT - this.span = MemoryMarshal.CreateSpan(ref array.DangerousGetReferenceAt(depth, row, column), height); -#else - this.Instance = array; - this.Offset = ObjectMarshal.DangerousGetObjectDataByteOffset(array, ref array.DangerousGetReferenceAt(depth, row, column)); - this.height = height; -#endif - this.width = width; - this.Stride = columns; - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// - /// Thrown when either or are invalid. - /// - /// The total area must match the length of . - internal Span2D(Span span, int height, int width) - : this(span, 0, height, width, 0) - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The target to wrap. - /// The initial offset within . - /// The height of the resulting 2D area. - /// The width of each row in the resulting 2D area. - /// The pitch in the resulting 2D area. - /// - /// Thrown when one of the input parameters is out of range. - /// - /// - /// Thrown when the requested area is outside of bounds for . - /// - internal Span2D(Span span, int offset, int height, int width, int pitch) - { - if ((uint)offset > (uint)span.Length) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - if (width == 0 || height == 0) - { - this = default; - - return; - } - - int - area = OverflowHelper.ComputeInt32Area(height, width, pitch), - remaining = span.Length - offset; - - if (area > remaining) - { - ThrowHelper.ThrowArgumentException(); - } - - this.span = MemoryMarshal.CreateSpan(ref span.DangerousGetReferenceAt(offset), height); - this.width = width; - this.Stride = width + pitch; - } - - /// - /// Creates a new instance of the struct with the specified parameters. - /// - /// The reference to the first item to map. - /// The height of the 2D memory area to map. - /// The width of the 2D memory area to map. - /// The pitch of the 2D memory area to map (the distance between each row). - /// A instance with the specified parameters. - /// Thrown when one of the parameters are negative. - [Pure] - public static Span2D DangerousCreate(ref T value, int height, int width, int pitch) - { - if (width < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - if (height < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if (pitch < 0) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForPitch(); - } - - OverflowHelper.EnsureIsInNativeIntRange(height, width, pitch); - - return new Span2D(ref value, height, width, pitch); - } -#endif - - /// - /// Gets an empty instance. - /// - public static Span2D Empty => default; - - /// - /// Gets a value indicating whether the current instance is empty. - /// - public bool IsEmpty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Height == 0 || this.width == 0; - } - - /// - /// Gets the length of the current instance. - /// - public nint Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (nint)(uint)Height * (nint)(uint)this.width; - } - - /// - /// Gets the height of the underlying 2D memory area. - /// - public int Height - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - return this.span.Length; -#else - return this.height; -#endif - } - } - - /// - /// Gets the width of the underlying 2D memory area. - /// - public int Width - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.width; - } - - /// - /// Gets the element at the specified zero-based indices. - /// - /// The target row to get the element from. - /// The target column to get the element from. - /// A reference to the element at the specified indices. - /// - /// Thrown when either or are invalid. - /// - public ref T this[int row, int column] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if ((uint)row >= (uint)Height || - (uint)column >= (uint)this.width) - { - ThrowHelper.ThrowIndexOutOfRangeException(); - } - - return ref DangerousGetReferenceAt(row, column); - } - } - -#if NETSTANDARD2_1_OR_GREATER - /// - /// Gets the element at the specified zero-based indices. - /// - /// The target row to get the element from. - /// The target column to get the element from. - /// A reference to the element at the specified indices. - /// - /// Thrown when either or are invalid. - /// - public ref T this[Index row, Index column] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref this[row.GetOffset(Height), column.GetOffset(this.width)]; - } - - /// - /// Slices the current instance with the specified parameters. - /// - /// The target range of rows to select. - /// The target range of columns to select. - /// - /// Thrown when either or are invalid. - /// - /// A new instance representing a slice of the current one. - public Span2D this[Range rows, Range columns] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - var (row, height) = rows.GetOffsetAndLength(Height); - var (column, width) = columns.GetOffsetAndLength(this.width); - - return Slice(row, column, height, width); - } - } -#endif - - /// - /// Clears the contents of the current instance. - /// - public void Clear() - { - if (IsEmpty) - { - return; - } - - if (TryGetSpan(out Span span)) - { - span.Clear(); - } - else - { - // Clear one row at a time -#if SPAN_RUNTIME_SUPPORT - for (int i = 0; i < Height; i++) - { - GetRowSpan(i).Clear(); - } -#else - int height = Height; - nint width = (nint)(uint)this.width; - - for (int i = 0; i < height; i++) - { - ref T rStart = ref DangerousGetReferenceAt(i, 0); - ref T rEnd = ref Unsafe.Add(ref rStart, width); - - while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) - { - rStart = default!; - - rStart = ref Unsafe.Add(ref rStart, 1); - } - } -#endif - } - } - - /// - /// Copies the contents of this into a destination instance. - /// - /// The destination instance. - /// - /// Thrown when is shorter than the source instance. - /// - public void CopyTo(Span destination) - { - if (IsEmpty) - { - return; - } - - if (TryGetSpan(out Span span)) - { - span.CopyTo(destination); - } - else - { - if (Length > destination.Length) - { - ThrowHelper.ThrowArgumentExceptionForDestinationTooShort(); - } - - // Copy each row individually -#if SPAN_RUNTIME_SUPPORT - for (int i = 0, j = 0; i < Height; i++, j += this.width) - { - GetRowSpan(i).CopyTo(destination.Slice(j)); - } -#else - int height = Height; - nint width = (nint)(uint)this.width; - - ref T destinationRef = ref MemoryMarshal.GetReference(destination); - - for (int i = 0; i < height; i++) - { - ref T sourceStart = ref DangerousGetReferenceAt(i, 0); - ref T sourceEnd = ref Unsafe.Add(ref sourceStart, width); - - while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) - { - destinationRef = sourceStart; - - sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destinationRef = ref Unsafe.Add(ref destinationRef, 1); - } - } -#endif - } - } - - /// - /// Copies the contents of this into a destination instance. - /// For this API to succeed, the target has to have the same shape as the current one. - /// - /// The destination instance. - /// - /// Thrown when does not have the same shape as the source instance. - /// - public void CopyTo(Span2D destination) - { - if (destination.Height != Height || - destination.width != this.width) - { - ThrowHelper.ThrowArgumentExceptionForDestinationWithNotSameShape(); - } - - if (IsEmpty) - { - return; - } - - if (destination.TryGetSpan(out Span span)) - { - CopyTo(span); - } - else - { - // Copy each row individually -#if SPAN_RUNTIME_SUPPORT - for (int i = 0; i < Height; i++) - { - GetRowSpan(i).CopyTo(destination.GetRowSpan(i)); - } -#else - int height = Height; - nint width = (nint)(uint)this.width; - - for (int i = 0; i < height; i++) - { - ref T sourceStart = ref DangerousGetReferenceAt(i, 0); - ref T sourceEnd = ref Unsafe.Add(ref sourceStart, width); - ref T destinationRef = ref destination.DangerousGetReferenceAt(i, 0); - - while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) - { - destinationRef = sourceStart; - - sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destinationRef = ref Unsafe.Add(ref destinationRef, 1); - } - } -#endif - } - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Span destination) - { - if (destination.Length >= Length) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - /// Attempts to copy the current instance to a destination . - /// - /// The target of the copy operation. - /// Whether or not the operation was successful. - public bool TryCopyTo(Span2D destination) - { - if (destination.Height == Height && - destination.Width == this.width) - { - CopyTo(destination); - - return true; - } - - return false; - } - - /// - /// Fills the elements of this span with a specified value. - /// - /// The value to assign to each element of the instance. - public void Fill(T value) - { - if (IsEmpty) - { - return; - } - - if (TryGetSpan(out Span span)) - { - span.Fill(value); - } - else - { - // Fill one row at a time -#if SPAN_RUNTIME_SUPPORT - for (int i = 0; i < Height; i++) - { - GetRowSpan(i).Fill(value); - } -#else - int height = Height; - nint width = (nint)(uint)this.width; - - for (int i = 0; i < height; i++) - { - ref T rStart = ref DangerousGetReferenceAt(i, 0); - ref T rEnd = ref Unsafe.Add(ref rStart, width); - - while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) - { - rStart = value; - - rStart = ref Unsafe.Add(ref rStart, 1); - } - } -#endif - } - } - - /// - /// Returns a reference to the 0th element of the instance. If the current - /// instance is empty, returns a reference. It can be used for pinning - /// and is required to support the use of span within a fixed statement. - /// - /// A reference to the 0th element, or a reference. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [EditorBrowsable(EditorBrowsableState.Never)] - public unsafe ref T GetPinnableReference() - { - ref T r0 = ref Unsafe.AsRef(null); - - if (Length != 0) - { -#if SPAN_RUNTIME_SUPPORT - r0 = ref MemoryMarshal.GetReference(this.span); -#else - r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); -#endif - } - - return ref r0; - } - - /// - /// Returns a reference to the first element within the current instance, with no bounds check. - /// - /// A reference to the first element within the current instance. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetReference() - { -#if SPAN_RUNTIME_SUPPORT - return ref MemoryMarshal.GetReference(this.span); -#else - return ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); -#endif - } - - /// - /// Returns a reference to a specified element within the current instance, with no bounds check. - /// - /// The target row to get the element from. - /// The target column to get the element from. - /// A reference to the element at the specified indices. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetReferenceAt(int i, int j) - { -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref MemoryMarshal.GetReference(this.span); -#else - ref T r0 = ref RuntimeHelpers.GetObjectDataAtOffsetOrPointerReference(this.Instance, this.Offset); -#endif - nint index = ((nint)(uint)i * (nint)(uint)this.Stride) + (nint)(uint)j; - - return ref Unsafe.Add(ref r0, index); - } - - /// - /// Slices the current instance with the specified parameters. - /// - /// The target row to map within the current instance. - /// The target column to map within the current instance. - /// The height to map within the current instance. - /// The width to map within the current instance. - /// - /// Thrown when either , or - /// are negative or not within the bounds that are valid for the current instance. - /// - /// A new instance representing a slice of the current one. - [Pure] - public Span2D Slice(int row, int column, int height, int width) - { - if ((uint)row >= Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - if ((uint)column >= this.width) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForColumn(); - } - - if ((uint)height > (Height - row)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForHeight(); - } - - if ((uint)width > (this.width - column)) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForWidth(); - } - - nint shift = ((nint)(uint)this.Stride * (nint)(uint)row) + (nint)(uint)column; - int pitch = this.Stride - width; - -#if SPAN_RUNTIME_SUPPORT - ref T r0 = ref this.span.DangerousGetReferenceAt(shift); - - return new Span2D(ref r0, height, width, pitch); -#else - IntPtr offset = this.Offset + (shift * (nint)(uint)Unsafe.SizeOf()); - - return new Span2D(this.Instance, offset, height, width, pitch); -#endif - } - -#if SPAN_RUNTIME_SUPPORT - /// - /// Gets a for a specified row. - /// - /// The index of the target row to retrieve. - /// Throw when is out of range. - /// The resulting row . - [Pure] - public Span GetRowSpan(int row) - { - if ((uint)row >= (uint)Height) - { - ThrowHelper.ThrowArgumentOutOfRangeExceptionForRow(); - } - - ref T r0 = ref DangerousGetReferenceAt(row, 0); - - return MemoryMarshal.CreateSpan(ref r0, this.width); - } -#endif - - /// - /// Tries to get a instance, if the underlying buffer is contiguous and small enough. - /// - /// The resulting , in case of success. - /// Whether or not was correctly assigned. - public bool TryGetSpan(out Span span) - { - // We can only create a Span if the buffer is contiguous - if (this.Stride == this.width && - Length <= int.MaxValue) - { -#if SPAN_RUNTIME_SUPPORT - span = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(this.span), (int)Length); - - return true; -#else - // An empty Span2D is still valid - if (IsEmpty) - { - span = default; - - return true; - } - - // Pinned Span2D - if (this.Instance is null) - { - unsafe - { - span = new Span((void*)this.Offset, (int)Length); - } - - return true; - } - - // Without Span runtime support, we can only get a Span from a T[] instance - if (this.Instance.GetType() == typeof(T[])) - { - T[] array = Unsafe.As(this.Instance)!; - int index = array.AsSpan().IndexOf(ref ObjectMarshal.DangerousGetObjectDataReferenceAt(array, this.Offset)); - - span = array.AsSpan(index, (int)Length); - - return true; - } -#endif - } - - span = default; - - return false; - } - - /// - /// Copies the contents of the current instance into a new 2D array. - /// - /// A 2D array containing the data in the current instance. - [Pure] - public T[,] ToArray() - { - T[,] array = new T[Height, this.width]; - -#if SPAN_RUNTIME_SUPPORT - CopyTo(array.AsSpan()); -#else - // Skip the initialization if the array is empty - if (Length > 0) - { - int height = Height; - nint width = (nint)(uint)this.width; - - ref T destinationRef = ref array.DangerousGetReference(); - - for (int i = 0; i < height; i++) - { - ref T sourceStart = ref DangerousGetReferenceAt(i, 0); - ref T sourceEnd = ref Unsafe.Add(ref sourceStart, width); - - while (Unsafe.IsAddressLessThan(ref sourceStart, ref sourceEnd)) - { - destinationRef = sourceStart; - - sourceStart = ref Unsafe.Add(ref sourceStart, 1); - destinationRef = ref Unsafe.Add(ref destinationRef, 1); - } - } - } -#endif - - return array; - } - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] - public override bool Equals(object? obj) - { - throw new NotSupportedException("Microsoft.Toolkit.HighPerformance.Span2D.Equals(object) is not supported"); - } - - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("GetHashCode() on Span will always throw an exception.")] - public override int GetHashCode() - { - throw new NotSupportedException("Microsoft.Toolkit.HighPerformance.Span2D.GetHashCode() is not supported"); - } - - /// - public override string ToString() - { - return $"Microsoft.Toolkit.HighPerformance.Span2D<{typeof(T)}>[{Height}, {this.width}]"; - } - - /// - /// Checks whether two instances are equal. - /// - /// The first instance to compare. - /// The second instance to compare. - /// Whether or not and are equal. - public static bool operator ==(Span2D left, Span2D right) - { - return -#if SPAN_RUNTIME_SUPPORT - left.span == right.span && -#else - ReferenceEquals(left.Instance, right.Instance) && - left.Offset == right.Offset && - left.height == right.height && -#endif - left.width == right.width && - left.Stride == right.Stride; - } - - /// - /// Checks whether two instances are not equal. - /// - /// The first instance to compare. - /// The second instance to compare. - /// Whether or not and are not equal. - public static bool operator !=(Span2D left, Span2D right) - { - return !(left == right); - } - - /// - /// Implicily converts a given 2D array into a instance. - /// - /// The input 2D array to convert. - public static implicit operator Span2D(T[,]? array) => new(array); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Memory/Views/MemoryDebugView2D{T}.cs b/Microsoft.Toolkit.HighPerformance/Memory/Views/MemoryDebugView2D{T}.cs deleted file mode 100644 index 9fb6a82e1b9..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Memory/Views/MemoryDebugView2D{T}.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; - -namespace Microsoft.Toolkit.HighPerformance.Memory.Views -{ - /// - /// A debug proxy used to display items in a 2D layout. - /// - /// The type of items to display. - internal sealed class MemoryDebugView2D - { - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView2D(Memory2D memory) - { - this.Items = memory.ToArray(); - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView2D(ReadOnlyMemory2D memory) - { - this.Items = memory.ToArray(); - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView2D(Span2D span) - { - this.Items = span.ToArray(); - } - - /// - /// Initializes a new instance of the class with the specified parameters. - /// - /// The input instance with the items to display. - public MemoryDebugView2D(ReadOnlySpan2D span) - { - this.Items = span.ToArray(); - } - - /// - /// Gets the items to display for the current instance - /// - [DebuggerBrowsable(DebuggerBrowsableState.Collapsed)] - public T[,]? Items { get; } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Microsoft.Toolkit.HighPerformance.csproj b/Microsoft.Toolkit.HighPerformance/Microsoft.Toolkit.HighPerformance.csproj deleted file mode 100644 index 1e8597bc417..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Microsoft.Toolkit.HighPerformance.csproj +++ /dev/null @@ -1,118 +0,0 @@ - - - - Enable - true - netstandard1.4;netstandard2.0;netstandard2.1;netcoreapp2.1;netcoreapp3.1;net5.0 - - - - Windows Community Toolkit - High Performance (.NET Standard) - - This package includes high performance .NET Standard helpers such as: - - Memory2D<T> and Span2D<T>: two types providing fast and allocation-free abstraction over 2D memory areas. - - ArrayPoolBufferWriter<T>: an IBufferWriter<T> implementation using pooled arrays, which also supports IMemoryOwner<T>. - - MemoryBufferWriter<T>: an IBufferWriter<T>: implementation that can wrap external Memory<T>: instances. - - MemoryOwner<T>: an IMemoryOwner<T> implementation with an embedded length and a fast Span<T> accessor. - - SpanOwner<T>: a stack-only type with the ability to rent a buffer of a specified length and getting a Span<T> from it. - - StringPool: a configurable pool for string instances that be used to minimize allocations when creating multiple strings from char buffers. - - String, array, Memory<T>, Span<T> extensions and more, all focused on high performance. - - HashCode<T>: a SIMD-enabled extension of HashCode to quickly process sequences of values. - - BitHelper: a class with helper methods to perform bit operations on numeric types. - - ParallelHelper: helpers to work with parallel code in a highly optimized manner. - - Box<T>: a type mapping boxed value types and exposing some utility and high performance methods. - - Ref<T>: a stack-only struct that can store a reference to a value of a specified type. - - NullableRef<T>: a stack-only struct similar to Ref<T>, which also supports nullable references. - - Parallel;Performance;Unsafe;Span;Memory;String;StringPool;Array;Stream;Buffer;Extensions;Helpers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NETSTANDARD2_1_OR_GREATER;SPAN_RUNTIME_SUPPORT - - - - - - - - - - NETSTANDARD2_1_OR_GREATER;SPAN_RUNTIME_SUPPORT - - - - - - - NETSTANDARD2_1_OR_GREATER;SPAN_RUNTIME_SUPPORT;NETCORE_RUNTIME - - - - - - - - - SPAN_RUNTIME_SUPPORT;NETCORE_RUNTIME - - - - - - - - \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/NullableReadOnlyRef{T}.cs b/Microsoft.Toolkit.HighPerformance/NullableReadOnlyRef{T}.cs deleted file mode 100644 index 623b14753aa..00000000000 --- a/Microsoft.Toolkit.HighPerformance/NullableReadOnlyRef{T}.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if SPAN_RUNTIME_SUPPORT - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// A that can store an optional readonly reference to a value of a specified type. - /// - /// The type of value to reference. - public readonly ref struct NullableReadOnlyRef - { - /// - /// The 1-length instance used to track the target value. - /// - private readonly ReadOnlySpan span; - - /// - /// Initializes a new instance of the struct. - /// - /// The readonly reference to the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NullableReadOnlyRef(in T value) - { - ref T r0 = ref Unsafe.AsRef(value); - - this.span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The instance to track the target reference. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private NullableReadOnlyRef(ReadOnlySpan span) - { - this.span = span; - } - - /// - /// Gets a instance representing a reference. - /// - public static NullableReadOnlyRef Null - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => default; - } - - /// - /// Gets a value indicating whether or not the current instance wraps a valid reference that can be accessed. - /// - public unsafe bool HasValue - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - // See comment in NullableRef about this - byte length = unchecked((byte)this.span.Length); - - return *(bool*)&length; - } - } - - /// - /// Gets the reference represented by the current instance. - /// - /// Thrown if is . - public ref readonly T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (!HasValue) - { - ThrowInvalidOperationException(); - } - - return ref MemoryMarshal.GetReference(this.span); - } - } - - /// - /// Implicitly converts a instance into a one. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator NullableReadOnlyRef(Ref reference) - { - return new(reference.Span); - } - - /// - /// Implicitly converts a instance into a one. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator NullableReadOnlyRef(ReadOnlyRef reference) - { - return new(reference.Span); - } - - /// - /// Implicitly converts a instance into a one. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator NullableReadOnlyRef(NullableRef reference) - { - return new(reference.Span); - } - - /// - /// Explicitly gets the value from a given instance. - /// - /// The input instance. - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator T(NullableReadOnlyRef reference) - { - return reference.Value; - } - - /// - /// Throws a when trying to access for a default instance. - /// - private static void ThrowInvalidOperationException() - { - throw new InvalidOperationException("The current instance doesn't have a value that can be accessed"); - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/NullableRef{T}.cs b/Microsoft.Toolkit.HighPerformance/NullableRef{T}.cs deleted file mode 100644 index 4e3443fe780..00000000000 --- a/Microsoft.Toolkit.HighPerformance/NullableRef{T}.cs +++ /dev/null @@ -1,123 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if SPAN_RUNTIME_SUPPORT - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// A that can store an optional reference to a value of a specified type. - /// - /// The type of value to reference. - public readonly ref struct NullableRef - { - /// - /// The 1-length instance used to track the target value. - /// - internal readonly Span Span; - - /// - /// Initializes a new instance of the struct. - /// - /// The reference to the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NullableRef(ref T value) - { - Span = MemoryMarshal.CreateSpan(ref value, 1); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The instance to track the target reference. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private NullableRef(Span span) - { - Span = span; - } - - /// - /// Gets a instance representing a reference. - /// - public static NullableRef Null - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => default; - } - - /// - /// Gets a value indicating whether or not the current instance wraps a valid reference that can be accessed. - /// - public unsafe bool HasValue - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - // We know that the span will always have a length of either - // 1 or 0, so instead of using a cmp instruction and setting the - // zero flag to produce our boolean value, we can just cast - // the length to byte without overflow checks (doing a cast will - // also account for the byte endianness of the current system), - // and then reinterpret that value to a bool flag. - // This results in a single movzx instruction on x86-64. - byte length = unchecked((byte)Span.Length); - - return *(bool*)&length; - } - } - - /// - /// Gets the reference represented by the current instance. - /// - /// Thrown if is . - public ref T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (!HasValue) - { - ThrowInvalidOperationException(); - } - - return ref MemoryMarshal.GetReference(Span); - } - } - - /// - /// Implicitly converts a instance into a one. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator NullableRef(Ref reference) - { - return new(reference.Span); - } - - /// - /// Explicitly gets the value from a given instance. - /// - /// The input instance. - /// Thrown if is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator T(NullableRef reference) - { - return reference.Value; - } - - /// - /// Throws a when trying to access for a default instance. - /// - private static void ThrowInvalidOperationException() - { - throw new InvalidOperationException("The current instance doesn't have a value that can be accessed"); - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Properties/AssemblyInfo.cs b/Microsoft.Toolkit.HighPerformance/Properties/AssemblyInfo.cs deleted file mode 100644 index 783438d33a8..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.CompilerServices; - -// We can suppress the .init flag for local variables for the entire module. -// This doesn't affect the correctness of methods in this assembly, as none of them -// are relying on the JIT ensuring that all local memory is zeroed out to work. Doing -// this can provide some minor performance benefits, depending on the workload. -[module: SkipLocalsInit] \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/ReadOnlyRef{T}.cs b/Microsoft.Toolkit.HighPerformance/ReadOnlyRef{T}.cs deleted file mode 100644 index 5dd65f35e43..00000000000 --- a/Microsoft.Toolkit.HighPerformance/ReadOnlyRef{T}.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -#else -using Microsoft.Toolkit.HighPerformance.Helpers; -#endif - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// A that can store a readonly reference to a value of a specified type. - /// - /// The type of value to reference. - public readonly ref struct ReadOnlyRef - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The 1-length instance used to track the target value. - /// - internal readonly ReadOnlySpan Span; - - /// - /// Initializes a new instance of the struct. - /// - /// The readonly reference to the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlyRef(in T value) - { - ref T r0 = ref Unsafe.AsRef(value); - - Span = MemoryMarshal.CreateReadOnlySpan(ref r0, 1); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The pointer to the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ReadOnlyRef(void* pointer) - : this(in Unsafe.AsRef(pointer)) - { - } - - /// - /// Gets the readonly reference represented by the current instance. - /// - public ref readonly T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref MemoryMarshal.GetReference(Span); - } - - /// - /// Implicitly converts a instance into a one. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlyRef(Ref reference) - { - return new(in reference.Value); - } -#else - /// - /// The owner the current instance belongs to - /// - private readonly object owner; - - /// - /// The target offset within the current instance is pointing to - /// - private readonly IntPtr offset; - - /// - /// Initializes a new instance of the struct. - /// - /// The owner to create a portable reference for. - /// The target offset within for the target reference. - /// The parameter is not validated, and it's responsibility of the caller to ensure it's valid. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ReadOnlyRef(object owner, IntPtr offset) - { - this.owner = owner; - this.offset = offset; - } - - /// - /// Initializes a new instance of the struct. - /// - /// The owner to create a portable reference for. - /// The target reference to point to (it must be within ). - /// The parameter is not validated, and it's responsibility of the caller to ensure it's valid. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlyRef(object owner, in T value) - { - this.owner = owner; - this.offset = ObjectMarshal.DangerousGetObjectDataByteOffset(owner, ref Unsafe.AsRef(value)); - } - - /// - /// Gets the readonly reference represented by the current instance. - /// - public ref readonly T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref ObjectMarshal.DangerousGetObjectDataReferenceAt(this.owner, this.offset); - } - - /// - /// Implicitly converts a instance into a one. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ReadOnlyRef(Ref reference) - { - return new(reference.Owner, reference.Offset); - } -#endif - - /// - /// Implicitly gets the value from a given instance. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator T(ReadOnlyRef reference) - { - return reference.Value; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Ref{T}.cs b/Microsoft.Toolkit.HighPerformance/Ref{T}.cs deleted file mode 100644 index bdfd6102d80..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Ref{T}.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -#else -using Microsoft.Toolkit.HighPerformance.Helpers; -#endif - -namespace Microsoft.Toolkit.HighPerformance -{ - /// - /// A that can store a reference to a value of a specified type. - /// - /// The type of value to reference. - public readonly ref struct Ref - { -#if SPAN_RUNTIME_SUPPORT - /// - /// The 1-length instance used to track the target value. - /// - internal readonly Span Span; - - /// - /// Initializes a new instance of the struct. - /// - /// The reference to the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Ref(ref T value) - { - Span = MemoryMarshal.CreateSpan(ref value, 1); - } - - /// - /// Initializes a new instance of the struct. - /// - /// The pointer to the target value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe Ref(void* pointer) - : this(ref Unsafe.AsRef(pointer)) - { - } - - /// - /// Gets the reference represented by the current instance. - /// - public ref T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref MemoryMarshal.GetReference(Span); - } -#else - /// - /// The owner the current instance belongs to - /// - internal readonly object Owner; - - /// - /// The target offset within the current instance is pointing to - /// - /// - /// Using an instead of to avoid the int to - /// native int conversion in the generated asm (an extra movsxd on x64). - /// - internal readonly IntPtr Offset; - - /// - /// Initializes a new instance of the struct. - /// - /// The owner to create a portable reference for. - /// The target reference to point to (it must be within ). - /// The parameter is not validated, and it's responsibility of the caller to ensure it's valid. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Ref(object owner, ref T value) - { - Owner = owner; - Offset = ObjectMarshal.DangerousGetObjectDataByteOffset(owner, ref value); - } - - /// - /// Gets the reference represented by the current instance. - /// - public ref T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref ObjectMarshal.DangerousGetObjectDataReferenceAt(Owner, Offset); - } -#endif - - /// - /// Implicitly gets the value from a given instance. - /// - /// The input instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator T(Ref reference) - { - return reference.Value; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/IBufferWriterStream{TWriter}.Memory.cs b/Microsoft.Toolkit.HighPerformance/Streams/IBufferWriterStream{TWriter}.Memory.cs deleted file mode 100644 index 14c03ff0ae1..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/IBufferWriterStream{TWriter}.Memory.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if SPAN_RUNTIME_SUPPORT - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - internal sealed partial class IBufferWriterStream - { - /// - public override void CopyTo(Stream destination, int bufferSize) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - if (cancellationToken.IsCancellationRequested) - { - return new ValueTask(Task.FromCanceled(cancellationToken)); - } - - try - { - Write(buffer.Span); - - return default; - } - catch (OperationCanceledException e) - { - return new ValueTask(Task.FromCanceled(e.CancellationToken)); - } - catch (Exception e) - { - return new ValueTask(Task.FromException(e)); - } - } - - /// - public override int Read(Span buffer) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override void Write(ReadOnlySpan buffer) - { - MemoryStream.ValidateDisposed(this.disposed); - - Span destination = this.bufferWriter.GetSpan(buffer.Length); - - if (!buffer.TryCopyTo(destination)) - { - MemoryStream.ThrowArgumentExceptionForEndOfStreamOnWrite(); - } - - this.bufferWriter.Advance(buffer.Length); - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/IBufferWriterStream{TWriter}.cs b/Microsoft.Toolkit.HighPerformance/Streams/IBufferWriterStream{TWriter}.cs deleted file mode 100644 index 1d0fbdf6342..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/IBufferWriterStream{TWriter}.cs +++ /dev/null @@ -1,173 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// A implementation wrapping an instance. - /// - /// The type of buffer writer to use. - internal sealed partial class IBufferWriterStream : Stream - where TWriter : struct, IBufferWriter - { - /// - /// The target instance to use. - /// - private readonly TWriter bufferWriter; - - /// - /// Indicates whether or not the current instance has been disposed - /// - private bool disposed; - - /// - /// Initializes a new instance of the class. - /// - /// The target instance to use. - public IBufferWriterStream(TWriter bufferWriter) - { - this.bufferWriter = bufferWriter; - } - - /// - public override bool CanRead => false; - - /// - public override bool CanSeek => false; - - /// - public override bool CanWrite - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => !this.disposed; - } - - /// - public override long Length => throw MemoryStream.GetNotSupportedException(); - - /// - public override long Position - { - get => throw MemoryStream.GetNotSupportedException(); - set => throw MemoryStream.GetNotSupportedException(); - } - - /// - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override void Flush() - { - } - - /// - public override Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - return Task.CompletedTask; - } - - /// - public override Task ReadAsync(byte[]? buffer, int offset, int count, CancellationToken cancellationToken) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override Task WriteAsync(byte[]? buffer, int offset, int count, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - try - { - Write(buffer, offset, count); - - return Task.CompletedTask; - } - catch (OperationCanceledException e) - { - return Task.FromCanceled(e.CancellationToken); - } - catch (Exception e) - { - return Task.FromException(e); - } - } - - /// - public override long Seek(long offset, SeekOrigin origin) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override void SetLength(long value) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override int Read(byte[]? buffer, int offset, int count) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override int ReadByte() - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public override void Write(byte[]? buffer, int offset, int count) - { - MemoryStream.ValidateDisposed(this.disposed); - MemoryStream.ValidateBuffer(buffer, offset, count); - - Span - source = buffer.AsSpan(offset, count), - destination = this.bufferWriter.GetSpan(count); - - if (!source.TryCopyTo(destination)) - { - MemoryStream.ThrowArgumentExceptionForEndOfStreamOnWrite(); - } - - this.bufferWriter.Advance(count); - } - - /// - public override void WriteByte(byte value) - { - MemoryStream.ValidateDisposed(this.disposed); - - this.bufferWriter.GetSpan(1)[0] = value; - - this.bufferWriter.Advance(1); - } - - /// - protected override void Dispose(bool disposing) - { - this.disposed = true; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/IMemoryOwnerStream{TSource}.cs b/Microsoft.Toolkit.HighPerformance/Streams/IMemoryOwnerStream{TSource}.cs deleted file mode 100644 index 55e11c683e8..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/IMemoryOwnerStream{TSource}.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.IO; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// A implementation wrapping an of instance. - /// - /// The type of source to use for the underlying data. - internal sealed class IMemoryOwnerStream : MemoryStream - where TSource : struct, ISpanOwner - { - /// - /// The instance currently in use. - /// - private readonly IDisposable disposable; - - /// - /// Initializes a new instance of the class. - /// - /// The input instance to use. - /// The instance currently in use. - public IMemoryOwnerStream(TSource source, IDisposable disposable) - : base(source, false) - { - this.disposable = disposable; - } - - /// - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - - this.disposable.Dispose(); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.ThrowExceptions.cs b/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.ThrowExceptions.cs deleted file mode 100644 index 9234e3f5c45..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.ThrowExceptions.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// A factory class to produce instances. - /// - internal static partial class MemoryStream - { - /// - /// Gets a standard instance for a stream. - /// - /// A with the standard text. - public static Exception GetNotSupportedException() - { - return new NotSupportedException("The requested operation is not supported for this stream."); - } - - /// - /// Throws a when trying to perform a not supported operation. - /// - public static void ThrowNotSupportedException() - { - throw GetNotSupportedException(); - } - - /// - /// Throws an when trying to write too many bytes to the target stream. - /// - public static void ThrowArgumentExceptionForEndOfStreamOnWrite() - { - throw new ArgumentException("The current stream can't contain the requested input data."); - } - - /// - /// Throws an when using an invalid seek mode. - /// - /// Nothing, as this method throws unconditionally. - public static long ThrowArgumentExceptionForSeekOrigin() - { - throw new ArgumentException("The input seek mode is not valid.", "origin"); - } - - /// - /// Throws an when setting the property. - /// - private static void ThrowArgumentOutOfRangeExceptionForPosition() - { - throw new ArgumentOutOfRangeException(nameof(Stream.Position), "The value for the property was not in the valid range."); - } - - /// - /// Throws an when an input buffer is . - /// - private static void ThrowArgumentNullExceptionForBuffer() - { - throw new ArgumentNullException("buffer", "The buffer is null."); - } - - /// - /// Throws an when the input count is negative. - /// - private static void ThrowArgumentOutOfRangeExceptionForOffset() - { - throw new ArgumentOutOfRangeException("offset", "Offset can't be negative."); - } - - /// - /// Throws an when the input count is negative. - /// - private static void ThrowArgumentOutOfRangeExceptionForCount() - { - throw new ArgumentOutOfRangeException("count", "Count can't be negative."); - } - - /// - /// Throws an when the sum of offset and count exceeds the length of the target buffer. - /// - private static void ThrowArgumentExceptionForLength() - { - throw new ArgumentException("The sum of offset and count can't be larger than the buffer length.", "buffer"); - } - - /// - /// Throws an when using a disposed instance. - /// - private static void ThrowObjectDisposedException() - { - throw new ObjectDisposedException("source", "The current stream has already been disposed"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.Validate.cs b/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.Validate.cs deleted file mode 100644 index 88514f696e2..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.Validate.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// A factory class to produce instances. - /// - internal static partial class MemoryStream - { - /// - /// Validates the argument (it needs to be in the [0, length]) range. - /// - /// The new value being set. - /// The maximum length of the target . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ValidatePosition(long position, int length) - { - if ((ulong)position > (ulong)length) - { - ThrowArgumentOutOfRangeExceptionForPosition(); - } - } - - /// - /// Validates the or arguments. - /// - /// The target array. - /// The offset within . - /// The number of elements to process within . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ValidateBuffer(byte[]? buffer, int offset, int count) - { - if (buffer is null) - { - ThrowArgumentNullExceptionForBuffer(); - } - - if (offset < 0) - { - ThrowArgumentOutOfRangeExceptionForOffset(); - } - - if (count < 0) - { - ThrowArgumentOutOfRangeExceptionForCount(); - } - - if (offset + count > buffer!.Length) - { - ThrowArgumentExceptionForLength(); - } - } - - /// - /// Validates the property. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ValidateCanWrite(bool canWrite) - { - if (!canWrite) - { - ThrowNotSupportedException(); - } - } - - /// - /// Validates that a given instance hasn't been disposed. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ValidateDisposed(bool disposed) - { - if (disposed) - { - ThrowObjectDisposedException(); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.cs b/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.cs deleted file mode 100644 index 565871f7163..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics.Contracts; -using System.IO; -using System.Runtime.InteropServices; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// A factory class to produce instances. - /// - internal static partial class MemoryStream - { - /// - /// Creates a new from the input of instance. - /// - /// The input instance. - /// Indicates whether or not can be written to. - /// A wrapping the underlying data for . - /// Thrown when has an invalid data store. - [Pure] - public static Stream Create(ReadOnlyMemory memory, bool isReadOnly) - { - if (memory.IsEmpty) - { - // Return an empty stream if the memory was empty - return new MemoryStream(ArrayOwner.Empty, isReadOnly); - } - - if (MemoryMarshal.TryGetArray(memory, out ArraySegment segment)) - { - var arraySpanSource = new ArrayOwner(segment.Array!, segment.Offset, segment.Count); - - return new MemoryStream(arraySpanSource, isReadOnly); - } - - if (MemoryMarshal.TryGetMemoryManager>(memory, out var memoryManager, out int start, out int length)) - { - MemoryManagerOwner memoryManagerSpanSource = new MemoryManagerOwner(memoryManager, start, length); - - return new MemoryStream(memoryManagerSpanSource, isReadOnly); - } - - return ThrowNotSupportedExceptionForInvalidMemory(); - } - - /// - /// Creates a new from the input of instance. - /// - /// The input instance. - /// A wrapping the underlying data for . - /// Thrown when has an invalid data store. - [Pure] - public static Stream Create(IMemoryOwner memoryOwner) - { - Memory memory = memoryOwner.Memory; - - if (memory.IsEmpty) - { - // Return an empty stream if the memory was empty - return new IMemoryOwnerStream(ArrayOwner.Empty, memoryOwner); - } - - if (MemoryMarshal.TryGetArray(memory, out ArraySegment segment)) - { - var arraySpanSource = new ArrayOwner(segment.Array!, segment.Offset, segment.Count); - - return new IMemoryOwnerStream(arraySpanSource, memoryOwner); - } - - if (MemoryMarshal.TryGetMemoryManager>(memory, out var memoryManager, out int start, out int length)) - { - MemoryManagerOwner memoryManagerSpanSource = new MemoryManagerOwner(memoryManager, start, length); - - return new IMemoryOwnerStream(memoryManagerSpanSource, memoryOwner); - } - - return ThrowNotSupportedExceptionForInvalidMemory(); - } - - /// - /// Throws a when a given - /// or instance has an unsupported backing store. - /// - /// Nothing, this method always throws. - private static Stream ThrowNotSupportedExceptionForInvalidMemory() - { - throw new ArgumentException("The input instance doesn't have a valid underlying data store."); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream{TSource}.Memory.cs b/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream{TSource}.Memory.cs deleted file mode 100644 index 845b1438d05..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream{TSource}.Memory.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if SPAN_RUNTIME_SUPPORT - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - internal partial class MemoryStream - { - /// - public sealed override void CopyTo(Stream destination, int bufferSize) - { - MemoryStream.ValidateDisposed(this.disposed); - - Span source = this.source.Span.Slice(this.position); - - this.position += source.Length; - - destination.Write(source); - } - - /// - public sealed override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - if (cancellationToken.IsCancellationRequested) - { - return new ValueTask(Task.FromCanceled(cancellationToken)); - } - - try - { - int result = Read(buffer.Span); - - return new ValueTask(result); - } - catch (OperationCanceledException e) - { - return new ValueTask(Task.FromCanceled(e.CancellationToken)); - } - catch (Exception e) - { - return new ValueTask(Task.FromException(e)); - } - } - - /// - public sealed override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - if (cancellationToken.IsCancellationRequested) - { - return new ValueTask(Task.FromCanceled(cancellationToken)); - } - - try - { - Write(buffer.Span); - - return default; - } - catch (OperationCanceledException e) - { - return new ValueTask(Task.FromCanceled(e.CancellationToken)); - } - catch (Exception e) - { - return new ValueTask(Task.FromException(e)); - } - } - - /// - public sealed override int Read(Span buffer) - { - MemoryStream.ValidateDisposed(this.disposed); - - int - bytesAvailable = this.source.Length - this.position, - bytesCopied = Math.Min(bytesAvailable, buffer.Length); - - Span source = this.source.Span.Slice(this.position, bytesCopied); - - source.CopyTo(buffer); - - this.position += bytesCopied; - - return bytesCopied; - } - - /// - public sealed override void Write(ReadOnlySpan buffer) - { - MemoryStream.ValidateDisposed(this.disposed); - MemoryStream.ValidateCanWrite(CanWrite); - - Span destination = this.source.Span.Slice(this.position); - - if (!buffer.TryCopyTo(destination)) - { - MemoryStream.ThrowArgumentExceptionForEndOfStreamOnWrite(); - } - - this.position += buffer.Length; - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream{TSource}.cs b/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream{TSource}.cs deleted file mode 100644 index 48ba4f63fdc..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/MemoryStream{TSource}.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// A implementation wrapping a or instance. - /// - /// The type of source to use for the underlying data. - /// - /// This type is not marked as so that it can be inherited by - /// , which adds the support for - /// the wrapped buffer. We're not worried about the performance penalty here caused by the JIT - /// not being able to resolve the instruction, as this type is - /// only exposed as a anyway, so the generated code would be the same. - /// - internal partial class MemoryStream : Stream - where TSource : struct, ISpanOwner - { - /// - /// Indicates whether can be written to. - /// - private readonly bool isReadOnly; - - /// - /// The instance currently in use. - /// - private TSource source; - - /// - /// The current position within . - /// - private int position; - - /// - /// Indicates whether or not the current instance has been disposed - /// - private bool disposed; - - /// - /// Initializes a new instance of the class. - /// - /// The input instance to use. - /// Indicates whether can be written to. - public MemoryStream(TSource source, bool isReadOnly) - { - this.source = source; - this.isReadOnly = isReadOnly; - } - - /// - public sealed override bool CanRead - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => !this.disposed; - } - - /// - public sealed override bool CanSeek - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => !this.disposed; - } - - /// - public sealed override bool CanWrite - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => !this.isReadOnly && !this.disposed; - } - - /// - public sealed override long Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - MemoryStream.ValidateDisposed(this.disposed); - - return this.source.Length; - } - } - - /// - public sealed override long Position - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - MemoryStream.ValidateDisposed(this.disposed); - - return this.position; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - MemoryStream.ValidateDisposed(this.disposed); - MemoryStream.ValidatePosition(value, this.source.Length); - - this.position = unchecked((int)value); - } - } - - /// - public sealed override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - try - { - CopyTo(destination, bufferSize); - - return Task.CompletedTask; - } - catch (OperationCanceledException e) - { - return Task.FromCanceled(e.CancellationToken); - } - catch (Exception e) - { - return Task.FromException(e); - } - } - - /// - public sealed override void Flush() - { - } - - /// - public sealed override Task FlushAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - return Task.CompletedTask; - } - - /// - public sealed override Task ReadAsync(byte[]? buffer, int offset, int count, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - try - { - int result = Read(buffer, offset, count); - - return Task.FromResult(result); - } - catch (OperationCanceledException e) - { - return Task.FromCanceled(e.CancellationToken); - } - catch (Exception e) - { - return Task.FromException(e); - } - } - - /// - public sealed override Task WriteAsync(byte[]? buffer, int offset, int count, CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - - try - { - Write(buffer, offset, count); - - return Task.CompletedTask; - } - catch (OperationCanceledException e) - { - return Task.FromCanceled(e.CancellationToken); - } - catch (Exception e) - { - return Task.FromException(e); - } - } - - /// - public sealed override long Seek(long offset, SeekOrigin origin) - { - MemoryStream.ValidateDisposed(this.disposed); - - long index = origin switch - { - SeekOrigin.Begin => offset, - SeekOrigin.Current => this.position + offset, - SeekOrigin.End => this.source.Length + offset, - _ => MemoryStream.ThrowArgumentExceptionForSeekOrigin() - }; - - MemoryStream.ValidatePosition(index, this.source.Length); - - this.position = unchecked((int)index); - - return index; - } - - /// - public sealed override void SetLength(long value) - { - throw MemoryStream.GetNotSupportedException(); - } - - /// - public sealed override int Read(byte[]? buffer, int offset, int count) - { - MemoryStream.ValidateDisposed(this.disposed); - MemoryStream.ValidateBuffer(buffer, offset, count); - - int - bytesAvailable = this.source.Length - this.position, - bytesCopied = Math.Min(bytesAvailable, count); - - Span - source = this.source.Span.Slice(this.position, bytesCopied), - destination = buffer.AsSpan(offset, bytesCopied); - - source.CopyTo(destination); - - this.position += bytesCopied; - - return bytesCopied; - } - - /// - public sealed override int ReadByte() - { - MemoryStream.ValidateDisposed(this.disposed); - - if (this.position == this.source.Length) - { - return -1; - } - - return this.source.Span[this.position++]; - } - - /// - public sealed override void Write(byte[]? buffer, int offset, int count) - { - MemoryStream.ValidateDisposed(this.disposed); - MemoryStream.ValidateCanWrite(CanWrite); - MemoryStream.ValidateBuffer(buffer, offset, count); - - Span - source = buffer.AsSpan(offset, count), - destination = this.source.Span.Slice(this.position); - - if (!source.TryCopyTo(destination)) - { - MemoryStream.ThrowArgumentExceptionForEndOfStreamOnWrite(); - } - - this.position += source.Length; - } - - /// - public sealed override void WriteByte(byte value) - { - MemoryStream.ValidateDisposed(this.disposed); - MemoryStream.ValidateCanWrite(CanWrite); - - if (this.position == this.source.Length) - { - MemoryStream.ThrowArgumentExceptionForEndOfStreamOnWrite(); - } - - this.source.Span[this.position++] = value; - } - - /// - protected override void Dispose(bool disposing) - { - if (this.disposed) - { - return; - } - - this.disposed = true; - this.source = default; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/Sources/ArrayBufferWriterOwner.cs b/Microsoft.Toolkit.HighPerformance/Streams/Sources/ArrayBufferWriterOwner.cs deleted file mode 100644 index be0ec40b8c2..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/Sources/ArrayBufferWriterOwner.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Buffers; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// An implementation wrapping an instance. - /// - internal readonly struct ArrayBufferWriterOwner : IBufferWriter - { - /// - /// The wrapped array. - /// - private readonly ArrayPoolBufferWriter writer; - - /// - /// Initializes a new instance of the struct. - /// - /// The wrapped instance. - public ArrayBufferWriterOwner(ArrayPoolBufferWriter writer) - { - this.writer = writer; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Advance(int count) - { - this.writer.Advance(count); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Memory GetMemory(int sizeHint = 0) - { - return this.writer.GetMemory(sizeHint); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetSpan(int sizeHint = 0) - { - return this.writer.GetSpan(sizeHint); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/Sources/ArrayOwner.cs b/Microsoft.Toolkit.HighPerformance/Streams/Sources/ArrayOwner.cs deleted file mode 100644 index 78a997a35de..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/Sources/ArrayOwner.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -#if SPAN_RUNTIME_SUPPORT -using System.Runtime.InteropServices; -#endif - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// An implementation wrapping an array. - /// - internal readonly struct ArrayOwner : ISpanOwner - { - /// - /// The wrapped array. - /// - private readonly byte[] array; - - /// - /// The starting offset within . - /// - private readonly int offset; - - /// - /// The usable length within . - /// - private readonly int length; - - /// - /// Initializes a new instance of the struct. - /// - /// The wrapped array. - /// The starting offset within . - /// The usable length within . - public ArrayOwner(byte[] array, int offset, int length) - { - this.array = array; - this.offset = offset; - this.length = length; - } - - /// - /// Gets an empty instance. - /// - public static ArrayOwner Empty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(Array.Empty(), 0, 0); - } - - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.length; - } - - /// - public Span Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { -#if SPAN_RUNTIME_SUPPORT - ref byte r0 = ref this.array.DangerousGetReferenceAt(this.offset); - - return MemoryMarshal.CreateSpan(ref r0, this.length); -#else - return this.array.AsSpan(this.offset, this.length); -#endif - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/Sources/IBufferWriterOwner.cs b/Microsoft.Toolkit.HighPerformance/Streams/Sources/IBufferWriterOwner.cs deleted file mode 100644 index 46f569f95e2..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/Sources/IBufferWriterOwner.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.HighPerformance.Buffers; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// An implementation wrapping an instance. - /// - internal readonly struct IBufferWriterOwner : IBufferWriter - { - /// - /// The wrapped array. - /// - private readonly IBufferWriter writer; - - /// - /// Initializes a new instance of the struct. - /// - /// The wrapped instance. - public IBufferWriterOwner(IBufferWriter writer) - { - this.writer = writer; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Advance(int count) - { - this.writer.Advance(count); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Memory GetMemory(int sizeHint = 0) - { - return this.writer.GetMemory(sizeHint); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetSpan(int sizeHint = 0) - { - return this.writer.GetSpan(sizeHint); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/Sources/Interfaces/ISpanOwner.cs b/Microsoft.Toolkit.HighPerformance/Streams/Sources/Interfaces/ISpanOwner.cs deleted file mode 100644 index 6a6e20ea223..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/Sources/Interfaces/ISpanOwner.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// An interface for types acting as sources for instances. - /// - internal interface ISpanOwner - { - /// - /// Gets the length of the underlying memory area. - /// - int Length { get; } - - /// - /// Gets a instance wrapping the underlying memory area. - /// - Span Span { get; } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.HighPerformance/Streams/Sources/MemoryManagerOwner.cs b/Microsoft.Toolkit.HighPerformance/Streams/Sources/MemoryManagerOwner.cs deleted file mode 100644 index 2e381d018da..00000000000 --- a/Microsoft.Toolkit.HighPerformance/Streams/Sources/MemoryManagerOwner.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.HighPerformance.Streams -{ - /// - /// An implementation wrapping a of instance. - /// - internal readonly struct MemoryManagerOwner : ISpanOwner - { - /// - /// The wrapped instance. - /// - private readonly MemoryManager memoryManager; - - /// - /// The starting offset within . - /// - private readonly int offset; - - /// - /// The usable length within . - /// - private readonly int length; - - /// - /// Initializes a new instance of the struct. - /// - /// The wrapped instance. - /// The starting offset within . - /// The usable length within . - public MemoryManagerOwner(MemoryManager memoryManager, int offset, int length) - { - this.memoryManager = memoryManager; - this.offset = offset; - this.length = length; - } - - /// - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.length; - } - - /// - public Span Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - // We can't use the same trick we use for arrays to optimize the creation of - // the offset span, as otherwise a bugged MemoryManager instance returning - // a span of an incorrect size could cause an access violation. Instead, we just - // get the span and then slice it, which will validate both offset and length. - return this.memoryManager.GetSpan().Slice(this.offset, this.length); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md b/Microsoft.Toolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md deleted file mode 100644 index 5ccc9f037f6..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/AnalyzerReleases.Shipped.md +++ /dev/null @@ -1,2 +0,0 @@ -; Shipped analyzer releases -; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/AnalyzerReleases.Unshipped.md b/Microsoft.Toolkit.Mvvm.SourceGenerators/AnalyzerReleases.Unshipped.md deleted file mode 100644 index 09be8eafd5f..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/AnalyzerReleases.Unshipped.md +++ /dev/null @@ -1,20 +0,0 @@ -; Unshipped analyzer release -; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md - -### New Rules - -Rule ID | Category | Severity | Notes ---------|----------|----------|------- -MVVMTK0001 | Microsoft.Toolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0002 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0003 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0004 | Microsoft.Toolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0005 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0006 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservableObjectGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0007 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0008 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0009 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0010 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0011 | Microsoft.Toolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0012 | Microsoft.Toolkit.Mvvm.SourceGenerators.ICommandGenerator | Error | See https://aka.ms/mvvmtoolkit/error -MVVMTK0013 | Microsoft.CodeAnalysis.CSharp.CSharpParseOptions | Error | See https://aka.ms/mvvmtoolkit/error diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NotNullWhenAttribute.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NotNullWhenAttribute.cs deleted file mode 100644 index 7e63f97aae5..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NotNullWhenAttribute.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - public NotNullWhenAttribute(bool returnValue) - { - ReturnValue = returnValue; - } - - /// - /// Gets a value indicating whether the annotated parameter will be null depending on the return value. - /// - public bool ReturnValue { get; } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs deleted file mode 100644 index a0c2d6ef8a2..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Attributes/NullabilityAttributesGenerator.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for necessary nullability attributes. - /// - [Generator] - public sealed class NullabilityAttributesGenerator : ISourceGenerator - { - /// - public void Initialize(GeneratorInitializationContext context) - { - } - - /// - public void Execute(GeneratorExecutionContext context) - { - AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullAttribute"); - AddSourceCodeIfTypeIsNotPresent(context, "System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute"); - } - - /// - /// Adds the source for a given attribute type if it's not present already in the compilation. - /// - private void AddSourceCodeIfTypeIsNotPresent(GeneratorExecutionContext context, string typeFullName) - { - // Check that the target attributes are not available in the consuming project. To ensure that - // this works fine both in .NET (Core) and .NET Standard implementations, we also need to check - // that the target types are declared as public (we assume that in this case those types are from the BCL). - // This avoids issues on .NET Standard with Roslyn also seeing internal types from referenced assemblies. - if (context.Compilation.GetTypeByMetadataName(typeFullName) is { DeclaredAccessibility: Accessibility.Public }) - { - return; - } - - string - typeName = typeFullName.Split('.').Last(), - filename = $"Microsoft.Toolkit.Mvvm.SourceGenerators.EmbeddedResources.{typeName}.cs"; - - Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename); - StreamReader reader = new(stream); - - string - originalSource = reader.ReadToEnd(), - outputSource = originalSource.Replace("NETSTANDARD2_0", "true"); - - context.AddSource($"{typeFullName}.cs", SourceText.From(outputSource, Encoding.UTF8)); - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs deleted file mode 100644 index 4f4534b5818..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/INotifyPropertyChangedGenerator.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for the INotifyPropertyChangedAttribute type. - /// - [Generator] - public sealed class INotifyPropertyChangedGenerator : TransitiveMembersGenerator - { - /// - /// Initializes a new instance of the class. - /// - public INotifyPropertyChangedGenerator() - : base("Microsoft.Toolkit.Mvvm.ComponentModel.INotifyPropertyChangedAttribute") - { - } - - /// - protected override DiagnosticDescriptor TargetTypeErrorDescriptor => INotifyPropertyChangedGeneratorError; - - /// - protected override bool ValidateTargetType( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - [NotNullWhen(false)] out DiagnosticDescriptor? descriptor) - { - INamedTypeSymbol iNotifyPropertyChangedSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged")!; - - // Check if the type already implements INotifyPropertyChanged - if (classDeclarationSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, iNotifyPropertyChangedSymbol))) - { - descriptor = DuplicateINotifyPropertyChangedInterfaceForINotifyPropertyChangedAttributeError; - - return false; - } - - descriptor = null; - - return true; - } - - /// - protected override IEnumerable FilterDeclaredMembers( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - ClassDeclarationSyntax sourceDeclaration) - { - // If requested, only include the event and the basic methods to raise it, but not the additional helpers - if (attributeData.HasNamedArgument("IncludeAdditionalHelperMethods", false)) - { - return sourceDeclaration.Members.Where(static member => - { - return member - is EventFieldDeclarationSyntax - or MethodDeclarationSyntax { Identifier: { ValueText: "OnPropertyChanged" } }; - }); - } - - return sourceDeclaration.Members; - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs deleted file mode 100644 index c1b1a37ffbc..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableObjectGenerator.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for the ObservableObjectAttribute type. - /// - [Generator] - public sealed class ObservableObjectGenerator : TransitiveMembersGenerator - { - /// - /// Initializes a new instance of the class. - /// - public ObservableObjectGenerator() - : base("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableObjectAttribute") - { - } - - /// - protected override DiagnosticDescriptor TargetTypeErrorDescriptor => ObservableObjectGeneratorError; - - /// - protected override bool ValidateTargetType( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - [NotNullWhen(false)] out DiagnosticDescriptor? descriptor) - { - INamedTypeSymbol - iNotifyPropertyChangedSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged")!, - iNotifyPropertyChangingSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanging")!; - - // Check if the type already implements INotifyPropertyChanged... - if (classDeclarationSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, iNotifyPropertyChangedSymbol))) - { - descriptor = DuplicateINotifyPropertyChangedInterfaceForObservableObjectAttributeError; - - return false; - } - - // ...or INotifyPropertyChanging - if (classDeclarationSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, iNotifyPropertyChangingSymbol))) - { - descriptor = DuplicateINotifyPropertyChangingInterfaceForObservableObjectAttributeError; - - return false; - } - - descriptor = null; - - return true; - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.SyntaxReceiver.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.SyntaxReceiver.cs deleted file mode 100644 index f71dfa367ac..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.SyntaxReceiver.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - public sealed partial class ObservablePropertyGenerator - { - /// - /// An that selects candidate nodes to process. - /// - private sealed class SyntaxReceiver : ISyntaxContextReceiver - { - /// - /// The list of info gathered during exploration. - /// - private readonly List gatheredInfo = new(); - - /// - /// Gets the collection of gathered info to process. - /// - public IReadOnlyCollection GatheredInfo => this.gatheredInfo; - - /// - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) - { - if (context.Node is FieldDeclarationSyntax { AttributeLists: { Count: > 0 } } fieldDeclaration && - context.SemanticModel.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is INamedTypeSymbol attributeSymbol) - { - SyntaxTriviaList leadingTrivia = fieldDeclaration.GetLeadingTrivia(); - - foreach (VariableDeclaratorSyntax variableDeclarator in fieldDeclaration.Declaration.Variables) - { - if (context.SemanticModel.GetDeclaredSymbol(variableDeclarator) is IFieldSymbol fieldSymbol && - fieldSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeSymbol))) - { - this.gatheredInfo.Add(new Item(leadingTrivia, fieldSymbol)); - } - } - } - } - - /// - /// A model for a group of item representing a discovered type to process. - /// - /// The leading trivia for the field declaration. - /// The instance for the target field. - public sealed record Item(SyntaxTriviaList LeadingTrivia, IFieldSymbol FieldSymbol); - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs deleted file mode 100644 index ef3ddf5b0f0..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs +++ /dev/null @@ -1,590 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using static Microsoft.CodeAnalysis.SymbolDisplayTypeQualificationStyle; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for the ObservablePropertyAttribute type. - /// - [Generator] - public sealed partial class ObservablePropertyGenerator : ISourceGenerator - { - /// - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(static () => new SyntaxReceiver()); - } - - /// - public void Execute(GeneratorExecutionContext context) - { - // Get the syntax receiver with the candidate nodes - if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver || - syntaxReceiver.GatheredInfo.Count == 0) - { - return; - } - - // Validate the language version. Note that we're emitting this diagnostic in each generator (excluding the one - // only emitting the nullability annotation attributes if missing) so that the diagnostic is emitted only when - // users are using one of these generators, and not by default as soon as they add a reference to the MVVM Toolkit. - // This ensures that users not using any of the source generators won't be broken when upgrading to this new version. - if (context.ParseOptions is not CSharpParseOptions { LanguageVersion: >= LanguageVersion.CSharp9 }) - { - context.ReportDiagnostic(Diagnostic.Create(UnsupportedCSharpLanguageVersionError, null)); - } - - // Sets of discovered property names - HashSet - propertyChangedNames = new(), - propertyChangingNames = new(); - - // Process the annotated fields - foreach (var items in syntaxReceiver.GatheredInfo.GroupBy(static item => item.FieldSymbol.ContainingType, SymbolEqualityComparer.Default)) - { - if (items.Key.DeclaringSyntaxReferences.Length > 0 && - items.Key.DeclaringSyntaxReferences.First().GetSyntax() is ClassDeclarationSyntax classDeclaration) - { - try - { - OnExecuteForProperties(context, classDeclaration, items.Key, items, propertyChangedNames, propertyChangingNames); - } - catch - { - context.ReportDiagnostic(ObservablePropertyGeneratorError, items.Key, items.Key); - } - } - } - - // Process the fields for the cached args - OnExecuteForPropertyArgs(context, propertyChangedNames, propertyChangingNames); - } - - /// - /// Processes a given target type for declared observable properties. - /// - /// The input instance to use. - /// The node to process. - /// The for . - /// The sequence of fields to process. - /// The collection of discovered property changed names. - /// The collection of discovered property changing names. - private static void OnExecuteForProperties( - GeneratorExecutionContext context, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - IEnumerable items, - ICollection propertyChangedNames, - ICollection propertyChangingNames) - { - INamedTypeSymbol - iNotifyPropertyChangingSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanging")!, - observableObjectSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableObject")!, - observableObjectAttributeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableObjectAttribute")!, - observableValidatorSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableValidator")!; - - // Check whether the current type implements INotifyPropertyChanging and whether it inherits from ObservableValidator - bool - isObservableObject = classDeclarationSymbol.InheritsFrom(observableObjectSymbol), - isObservableValidator = classDeclarationSymbol.InheritsFrom(observableValidatorSymbol), - isNotifyPropertyChanging = - isObservableObject || - classDeclarationSymbol.AllInterfaces.Contains(iNotifyPropertyChangingSymbol, SymbolEqualityComparer.Default) || - classDeclarationSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, observableObjectAttributeSymbol)); - - // Create the class declaration for the user type. This will produce a tree as follows: - // - // - // { - // - // } - var classDeclarationSyntax = - ClassDeclaration(classDeclarationSymbol.Name) - .WithModifiers(classDeclaration.Modifiers) - .AddMembers(items.Select(item => - CreatePropertyDeclaration( - context, - item.LeadingTrivia, - item.FieldSymbol, - isNotifyPropertyChanging, - isObservableValidator, - propertyChangedNames, - propertyChangingNames)).ToArray()); - - TypeDeclarationSyntax typeDeclarationSyntax = classDeclarationSyntax; - - // Add all parent types in ascending order, if any - foreach (var parentType in classDeclaration.Ancestors().OfType()) - { - typeDeclarationSyntax = parentType - .WithMembers(SingletonList(typeDeclarationSyntax)) - .WithConstraintClauses(List()) - .WithBaseList(null) - .WithAttributeLists(List()) - .WithoutTrivia(); - } - - // Create the compilation unit with the namespace and target member. - // From this, we can finally generate the source code to output. - var namespaceName = classDeclarationSymbol.ContainingNamespace.ToDisplayString(new(typeQualificationStyle: NameAndContainingTypesAndNamespaces)); - - // Create the final compilation unit to generate (with leading trivia) - var source = - CompilationUnit().AddMembers( - NamespaceDeclaration(IdentifierName(namespaceName)).WithLeadingTrivia(TriviaList( - Comment("// Licensed to the .NET Foundation under one or more agreements."), - Comment("// The .NET Foundation licenses this file to you under the MIT license."), - Comment("// See the LICENSE file in the project root for more information."), - Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)))) - .AddMembers(typeDeclarationSyntax)) - .NormalizeWhitespace() - .ToFullString(); - - // Add the partial type - context.AddSource($"{classDeclarationSymbol.GetFullMetadataNameForFileName()}.cs", SourceText.From(source, Encoding.UTF8)); - } - - /// - /// Creates a instance for a specified field. - /// - /// The input instance to use. - /// The leading trivia for the field to process. - /// The input instance to process. - /// Indicates whether or not is also implemented. - /// Indicates whether or not the containing type inherits from ObservableValidator. - /// The collection of discovered property changed names. - /// The collection of discovered property changing names. - /// A generated instance for the input field. - [Pure] - private static PropertyDeclarationSyntax CreatePropertyDeclaration( - GeneratorExecutionContext context, - SyntaxTriviaList leadingTrivia, - IFieldSymbol fieldSymbol, - bool isNotifyPropertyChanging, - bool isObservableValidator, - ICollection propertyChangedNames, - ICollection propertyChangingNames) - { - // Get the field type and the target property name - string - typeName = fieldSymbol.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - propertyName = GetGeneratedPropertyName(fieldSymbol); - - INamedTypeSymbol alsoNotifyChangeForAttributeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.AlsoNotifyChangeForAttribute")!; - INamedTypeSymbol? validationAttributeSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute"); - - List dependentPropertyNotificationStatements = new(); - List validationAttributes = new(); - - foreach (AttributeData attributeData in fieldSymbol.GetAttributes()) - { - // Add dependent property notifications, if needed - if (SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, alsoNotifyChangeForAttributeSymbol)) - { - foreach (TypedConstant attributeArgument in attributeData.ConstructorArguments) - { - if (attributeArgument.IsNull) - { - continue; - } - - if (attributeArgument.Kind == TypedConstantKind.Primitive && - attributeArgument.Value is string dependentPropertyName) - { - propertyChangedNames.Add(dependentPropertyName); - - // OnPropertyChanged("OtherPropertyName"); - dependentPropertyNotificationStatements.Add(ExpressionStatement( - InvocationExpression(IdentifierName("OnPropertyChanged")) - .AddArgumentListArguments(Argument(MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs"), - IdentifierName($"{dependentPropertyName}{nameof(PropertyChangedEventArgs)}")))))); - } - else if (attributeArgument.Kind == TypedConstantKind.Array) - { - foreach (TypedConstant nestedAttributeArgument in attributeArgument.Values) - { - if (nestedAttributeArgument.IsNull) - { - continue; - } - - string currentPropertyName = (string)nestedAttributeArgument.Value!; - - propertyChangedNames.Add(currentPropertyName); - - // Additional property names - dependentPropertyNotificationStatements.Add(ExpressionStatement( - InvocationExpression(IdentifierName("OnPropertyChanged")) - .AddArgumentListArguments(Argument(MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs"), - IdentifierName($"{currentPropertyName}{nameof(PropertyChangedEventArgs)}")))))); - } - } - } - } - else if (validationAttributeSymbol is not null && - attributeData.AttributeClass?.InheritsFrom(validationAttributeSymbol) == true) - { - // Track the current validation attribute - validationAttributes.Add(attributeData.AsAttributeSyntax()); - } - } - - // In case the backing field is exactly named "value", we need to add the "this." prefix to ensure that comparisons and assignments - // with it in the generated setter body are executed correctly and without conflicts with the implicit value parameter. - ExpressionSyntax fieldExpression = fieldSymbol.Name switch - { - "value" => MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, ThisExpression(), IdentifierName("value")), - string name => IdentifierName(name) - }; - - BlockSyntax setterBlock; - - if (validationAttributes.Count > 0) - { - // Emit a diagnostic if the current type doesn't inherit from ObservableValidator - if (!isObservableValidator) - { - context.ReportDiagnostic( - MissingObservableValidatorInheritanceError, - fieldSymbol, - fieldSymbol.ContainingType, - fieldSymbol.Name, - validationAttributes.Count); - - setterBlock = Block(); - } - else - { - propertyChangedNames.Add(propertyName); - propertyChangingNames.Add(propertyName); - - // Generate the inner setter block as follows: - // - // if (!global::System.Collections.Generic.EqualityComparer<>.Default.Equals(this., value)) - // { - // OnPropertyChanging(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangingEventArgs); // Optional - // this. = value; - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangedEventArgs); - // ValidateProperty(value, ); - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.Property1PropertyChangedEventArgs); // Optional - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.Property2PropertyChangedEventArgs); - // ... - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNPropertyChangedEventArgs); - // } - // - // The reason why the code is explicitly generated instead of just calling ObservableValidator.SetProperty() is so that we can - // take advantage of the cached property changed arguments for the current property as well, not just for the dependent ones. - setterBlock = Block( - IfStatement( - PrefixUnaryExpression( - SyntaxKind.LogicalNotExpression, - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - GenericName(Identifier("global::System.Collections.Generic.EqualityComparer")) - .AddTypeArgumentListArguments(IdentifierName(typeName)), - IdentifierName("Default")), - IdentifierName("Equals"))) - .AddArgumentListArguments( - Argument(fieldExpression), - Argument(IdentifierName("value")))), - Block( - ExpressionStatement( - InvocationExpression(IdentifierName("OnPropertyChanging")) - .AddArgumentListArguments(Argument(MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs"), - IdentifierName($"{propertyName}{nameof(PropertyChangingEventArgs)}"))))), - ExpressionStatement( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - fieldExpression, - IdentifierName("value"))), - ExpressionStatement( - InvocationExpression(IdentifierName("OnPropertyChanged")) - .AddArgumentListArguments(Argument(MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs"), - IdentifierName($"{propertyName}{nameof(PropertyChangedEventArgs)}"))))), - ExpressionStatement( - InvocationExpression(IdentifierName("ValidateProperty")) - .AddArgumentListArguments( - Argument(IdentifierName("value")), - Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(propertyName)))))) - .AddStatements(dependentPropertyNotificationStatements.ToArray()))); - } - } - else - { - BlockSyntax updateAndNotificationBlock = Block(); - - // Add OnPropertyChanging(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangingEventArgs) if necessary - if (isNotifyPropertyChanging) - { - propertyChangingNames.Add(propertyName); - - updateAndNotificationBlock = updateAndNotificationBlock.AddStatements(ExpressionStatement( - InvocationExpression(IdentifierName("OnPropertyChanging")) - .AddArgumentListArguments(Argument(MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs"), - IdentifierName($"{propertyName}{nameof(PropertyChangingEventArgs)}")))))); - } - - propertyChangedNames.Add(propertyName); - - // Add the following statements: - // - // = value; - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangedEventArgs); - updateAndNotificationBlock = updateAndNotificationBlock.AddStatements( - ExpressionStatement( - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - fieldExpression, - IdentifierName("value"))), - ExpressionStatement( - InvocationExpression(IdentifierName("OnPropertyChanged")) - .AddArgumentListArguments(Argument(MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs"), - IdentifierName($"{propertyName}{nameof(PropertyChangedEventArgs)}")))))); - - // Add the dependent property notifications at the end - updateAndNotificationBlock = updateAndNotificationBlock.AddStatements(dependentPropertyNotificationStatements.ToArray()); - - // Generate the inner setter block as follows: - // - // if (!global::System.Collections.Generic.EqualityComparer<>.Default.Equals(, value)) - // { - // OnPropertyChanging(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangingEventArgs); // Optional - // = value; - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNamePropertyChangedEventArgs); - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.Property1PropertyChangedEventArgs); // Optional - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.Property2PropertyChangedEventArgs); - // ... - // OnPropertyChanged(global::Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedOrChangingArgs.PropertyNPropertyChangedEventArgs); - // } - setterBlock = Block( - IfStatement( - PrefixUnaryExpression( - SyntaxKind.LogicalNotExpression, - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - GenericName(Identifier("global::System.Collections.Generic.EqualityComparer")) - .AddTypeArgumentListArguments(IdentifierName(typeName)), - IdentifierName("Default")), - IdentifierName("Equals"))) - .AddArgumentListArguments( - Argument(fieldExpression), - Argument(IdentifierName("value")))), - updateAndNotificationBlock)); - } - - // Get the right type for the declared property (including nullability annotations) - TypeSyntax propertyType = IdentifierName(typeName); - - if (fieldSymbol.Type is { IsReferenceType: true, NullableAnnotation: NullableAnnotation.Annotated }) - { - propertyType = NullableType(propertyType); - } - - // Construct the generated property as follows: - // - // - // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] - // [global::System.Diagnostics.DebuggerNonUserCode] - // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - // // Optional - // - // ... - // - // public - // { - // get => ; - // set - // { - // - // } - // } - return - PropertyDeclaration(propertyType, Identifier(propertyName)) - .AddAttributeLists( - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.CodeDom.Compiler.GeneratedCode")) - .AddArgumentListArguments( - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservablePropertyGenerator).FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ObservablePropertyGenerator).Assembly.GetName().Version.ToString())))))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))))) - .AddAttributeLists(validationAttributes.Select(static a => AttributeList(SingletonSeparatedList(a))).ToArray()) - .WithLeadingTrivia(leadingTrivia.Where(static trivia => !trivia.IsKind(SyntaxKind.RegionDirectiveTrivia) && !trivia.IsKind(SyntaxKind.EndRegionDirectiveTrivia))) - .AddModifiers(Token(SyntaxKind.PublicKeyword)) - .AddAccessorListAccessors( - AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) - .WithExpressionBody(ArrowExpressionClause(IdentifierName(fieldSymbol.Name))) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)), - AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) - .WithBody(setterBlock)); - } - - /// - /// Get the generated property name for an input field. - /// - /// The input instance to process. - /// The generated property name for . - [Pure] - public static string GetGeneratedPropertyName(IFieldSymbol fieldSymbol) - { - string propertyName = fieldSymbol.Name; - - if (propertyName.StartsWith("m_")) - { - propertyName = propertyName.Substring(2); - } - else if (propertyName.StartsWith("_")) - { - propertyName = propertyName.TrimStart('_'); - } - - return $"{char.ToUpper(propertyName[0])}{propertyName.Substring(1)}"; - } - - /// - /// Processes the cached property changed/changing args. - /// - /// The input instance to use. - /// The collection of discovered property changed names. - /// The collection of discovered property changing names. - public void OnExecuteForPropertyArgs(GeneratorExecutionContext context, IReadOnlyCollection propertyChangedNames, IReadOnlyCollection propertyChangingNames) - { - if (propertyChangedNames.Count == 0 && - propertyChangingNames.Count == 0) - { - return; - } - - INamedTypeSymbol - propertyChangedEventArgsSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.PropertyChangedEventArgs")!, - propertyChangingEventArgsSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.PropertyChangingEventArgs")!; - - // Create a static method to validate all properties in a given class. - // This code takes a class symbol and produces a compilation unit as follows: - // - // // Licensed to the .NET Foundation under one or more agreements. - // // The .NET Foundation licenses this file to you under the MIT license. - // // See the LICENSE file in the project root for more information. - // - // #pragma warning disable - // - // namespace Microsoft.Toolkit.Mvvm.ComponentModel.__Internals - // { - // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] - // [global::System.Diagnostics.DebuggerNonUserCode] - // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - // [global::System.Obsolete("This type is not intended to be used directly by user code")] - // internal static class __KnownINotifyPropertyChangedOrChangingArgs - // { - // - // } - // } - var source = - CompilationUnit().AddMembers( - NamespaceDeclaration(IdentifierName("Microsoft.Toolkit.Mvvm.ComponentModel.__Internals")).WithLeadingTrivia(TriviaList( - Comment("// Licensed to the .NET Foundation under one or more agreements."), - Comment("// The .NET Foundation licenses this file to you under the MIT license."), - Comment("// See the LICENSE file in the project root for more information."), - Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)))).AddMembers( - ClassDeclaration("__KnownINotifyPropertyChangedOrChangingArgs").AddModifiers( - Token(SyntaxKind.InternalKeyword), - Token(SyntaxKind.StaticKeyword)).AddAttributeLists( - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")) - .AddArgumentListArguments( - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().Assembly.GetName().Version.ToString())))))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal("This type is not intended to be used directly by user code"))))))) - .AddMembers(propertyChangedNames.Select(name => CreateFieldDeclaration(propertyChangedEventArgsSymbol, name)).ToArray()) - .AddMembers(propertyChangingNames.Select(name => CreateFieldDeclaration(propertyChangingEventArgsSymbol, name)).ToArray()))) - .NormalizeWhitespace() - .ToFullString(); - - // Add the partial type - context.AddSource("__KnownINotifyPropertyChangedOrChangingArgs.cs", SourceText.From(source, Encoding.UTF8)); - } - - /// - /// Creates a field declaration for a cached property change name. - /// - /// The type of cached property change argument (either or ). - /// The name of the cached property name. - /// A instance for the input cached property name. - [Pure] - private static FieldDeclarationSyntax CreateFieldDeclaration(INamedTypeSymbol type, string propertyName) - { - // Create a static field with a cached property changed/changing argument for a specified property. - // This code produces a field declaration as follows: - // - // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - // [global::System.Obsolete("This field is not intended to be referenced directly by user code")] - // public static readonly = new(""); - return - FieldDeclaration( - VariableDeclaration(IdentifierName(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))) - .AddVariables( - VariableDeclarator(Identifier($"{propertyName}{type.Name}")) - .WithInitializer(EqualsValueClause( - ImplicitObjectCreationExpression() - .AddArgumentListArguments(Argument( - LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(propertyName)))))))) - .AddModifiers( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.StaticKeyword), - Token(SyntaxKind.ReadOnlyKeyword)) - .AddAttributeLists( - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal("This field is not intended to be referenced directly by user code"))))))); - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs deleted file mode 100644 index 7a2228bfde0..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for the ObservableRecipientAttribute type. - /// - [Generator] - public sealed class ObservableRecipientGenerator : TransitiveMembersGenerator - { - /// - /// Initializes a new instance of the class. - /// - public ObservableRecipientGenerator() - : base("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableRecipientAttribute") - { - } - - /// - protected override DiagnosticDescriptor TargetTypeErrorDescriptor => ObservableRecipientGeneratorError; - - /// - protected override bool ValidateTargetType( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - [NotNullWhen(false)] out DiagnosticDescriptor? descriptor) - { - INamedTypeSymbol - observableRecipientSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableRecipient")!, - observableObjectSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableObject")!, - observableObjectAttributeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableObjectAttribute")!, - iNotifyPropertyChangedSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged")!; - - // Check if the type already inherits from ObservableRecipient - if (classDeclarationSymbol.InheritsFrom(observableRecipientSymbol)) - { - descriptor = DuplicateObservableRecipientError; - - return false; - } - - // In order to use [ObservableRecipient], the target type needs to inherit from ObservableObject, - // or be annotated with [ObservableObject] or [INotifyPropertyChanged] (with additional helpers). - if (!classDeclarationSymbol.InheritsFrom(observableObjectSymbol) && - !classDeclarationSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, observableObjectAttributeSymbol)) && - !classDeclarationSymbol.GetAttributes().Any(a => - SymbolEqualityComparer.Default.Equals(a.AttributeClass, iNotifyPropertyChangedSymbol) && - !a.HasNamedArgument("IncludeAdditionalHelperMethods", false))) - { - descriptor = MissingBaseObservableObjectFunctionalityError; - - return false; - } - - descriptor = null; - - return true; - } - - /// - protected override IEnumerable FilterDeclaredMembers( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - ClassDeclarationSyntax sourceDeclaration) - { - // If the target type has no constructors, generate constructors as well - if (classDeclarationSymbol.InstanceConstructors.Length == 1 && - classDeclarationSymbol.InstanceConstructors[0] is - { - Parameters: { IsEmpty: true }, - DeclaringSyntaxReferences: { IsEmpty: true }, - IsImplicitlyDeclared: true - }) - { - foreach (ConstructorDeclarationSyntax ctor in sourceDeclaration.Members.OfType()) - { - string - text = ctor.NormalizeWhitespace().ToFullString(), - replaced = text.Replace("ObservableRecipient", classDeclarationSymbol.Name); - - // Adjust the visibility of the constructors based on whether the target type is abstract. - // If that is not the case, the constructors have to be declared as public and not protected. - if (!classDeclarationSymbol.IsAbstract) - { - replaced = replaced.Replace("protected", "public"); - } - - yield return (ConstructorDeclarationSyntax)ParseMemberDeclaration(replaced)!; - } - } - - INamedTypeSymbol observableValidatorSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableValidator")!; - - // Skip the SetProperty overloads if the target type inherits from ObservableValidator, to avoid conflicts - if (classDeclarationSymbol.InheritsFrom(observableValidatorSymbol)) - { - foreach (MemberDeclarationSyntax member in sourceDeclaration.Members.Where(static member => member is not ConstructorDeclarationSyntax)) - { - if (member is not MethodDeclarationSyntax { Identifier: { ValueText: "SetProperty" } }) - { - yield return member; - } - } - - yield break; - } - - // If the target type has at least one custom constructor, only generate methods - foreach (MemberDeclarationSyntax member in sourceDeclaration.Members.Where(static member => member is not ConstructorDeclarationSyntax)) - { - yield return member; - } - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.SyntaxReceiver.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.SyntaxReceiver.cs deleted file mode 100644 index ece1653b246..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.SyntaxReceiver.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - public sealed partial class ObservableValidatorValidateAllPropertiesGenerator - { - /// - /// An that selects candidate nodes to process. - /// - private sealed class SyntaxReceiver : ISyntaxContextReceiver - { - /// - /// The list of info gathered during exploration. - /// - private readonly List gatheredInfo = new(); - - /// - /// Gets the collection of gathered info to process. - /// - public IReadOnlyCollection GatheredInfo => this.gatheredInfo; - - /// - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) - { - if (context.Node is ClassDeclarationSyntax classDeclaration && - context.SemanticModel.GetDeclaredSymbol(classDeclaration) is INamedTypeSymbol { IsGenericType: false } classSymbol && - context.SemanticModel.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservableValidator") is INamedTypeSymbol validatorSymbol && - classSymbol.InheritsFrom(validatorSymbol)) - { - this.gatheredInfo.Add(classSymbol); - } - } - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs deleted file mode 100644 index dd68fa72bba..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/ObservableValidatorValidateAllPropertiesGenerator.cs +++ /dev/null @@ -1,245 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -#pragma warning disable SA1008 - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for properties validation without relying on compiled LINQ expressions. - /// - [Generator] - public sealed partial class ObservableValidatorValidateAllPropertiesGenerator : ISourceGenerator - { - /// - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(static () => new SyntaxReceiver()); - } - - /// - public void Execute(GeneratorExecutionContext context) - { - // Get the syntax receiver with the candidate nodes - if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver || - syntaxReceiver.GatheredInfo.Count == 0) - { - return; - } - - // Validate the language version (this needs at least C# 8.0 due to static local functions being used). - // If a lower C# version is set, just skip the execution silently. The fallback path will be used just fine. - if (context.ParseOptions is not CSharpParseOptions { LanguageVersion: >= LanguageVersion.CSharp8 }) - { - return; - } - - // Get the symbol for the required attributes - INamedTypeSymbol - validationSymbol = context.Compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.ValidationAttribute")!, - observablePropertySymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.ComponentModel.ObservablePropertyAttribute")!; - - // Prepare the attributes to add to the first class declaration - AttributeListSyntax[] classAttributes = new[] - { - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")) - .AddArgumentListArguments( - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().Assembly.GetName().Version.ToString())))))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal("This type is not intended to be used directly by user code")))))) - }; - - foreach (INamedTypeSymbol classSymbol in syntaxReceiver.GatheredInfo) - { - // Create a static factory method creating a delegate that can be used to validate all properties in a given class. - // This pattern is used so that the library doesn't have to use MakeGenericType(...) at runtime, nor use unsafe casts - // over the created delegate to be able to cache it as an Action instance. This pattern enables the same - // functionality and with almost identical performance (not noticeable in this context anyway), but while preserving - // full runtime type safety (as a safe cast is used to validate the input argument), and with less reflection needed. - // Note that we're deliberately creating a new delegate instance here and not using code that could see the C# compiler - // create a static class to cache a reusable delegate, because each generated method will only be called at most once, - // as the returned delegate will be cached by the MVVM Toolkit itself. So this ensures the the produced code is minimal, - // and that there will be no unnecessary static fields and objects being created and possibly never collected. - // This code takes a class symbol and produces a compilation unit as follows: - // - // // Licensed to the .NET Foundation under one or more agreements. - // // The .NET Foundation licenses this file to you under the MIT license. - // // See the LICENSE file in the project root for more information. - // - // #pragma warning disable - // - // namespace Microsoft.Toolkit.Mvvm.ComponentModel.__Internals - // { - // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] - // [global::System.Diagnostics.DebuggerNonUserCode] - // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - // [global::System.Obsolete("This type is not intended to be used directly by user code")] - // internal static partial class __ObservableValidatorExtensions - // { - // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - // [global::System.Obsolete("This method is not intended to be called directly by user code")] - // public static global::System.Action CreateAllPropertiesValidator( _) - // { - // static void ValidateAllProperties(object obj) - // { - // var instance = ()obj; - // - // } - // - // return ValidateAllProperties; - // } - // } - // } - var source = - CompilationUnit().AddMembers( - NamespaceDeclaration(IdentifierName("Microsoft.Toolkit.Mvvm.ComponentModel.__Internals")).WithLeadingTrivia(TriviaList( - Comment("// Licensed to the .NET Foundation under one or more agreements."), - Comment("// The .NET Foundation licenses this file to you under the MIT license."), - Comment("// See the LICENSE file in the project root for more information."), - Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)))).AddMembers( - ClassDeclaration("__ObservableValidatorExtensions").AddModifiers( - Token(SyntaxKind.InternalKeyword), - Token(SyntaxKind.StaticKeyword), - Token(SyntaxKind.PartialKeyword)).AddAttributeLists(classAttributes).AddMembers( - MethodDeclaration( - GenericName("global::System.Action").AddTypeArgumentListArguments(PredefinedType(Token(SyntaxKind.ObjectKeyword))), - Identifier("CreateAllPropertiesValidator")).AddAttributeLists( - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal("This method is not intended to be called directly by user code"))))))).AddModifiers( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.StaticKeyword)).AddParameterListParameters( - Parameter(Identifier("_")).WithType(IdentifierName(classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))) - .WithBody(Block( - LocalFunctionStatement( - PredefinedType(Token(SyntaxKind.VoidKeyword)), - Identifier("ValidateAllProperties")) - .AddModifiers(Token(SyntaxKind.StaticKeyword)) - .AddParameterListParameters( - Parameter(Identifier("obj")).WithType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))) - .WithBody(Block( - LocalDeclarationStatement( - VariableDeclaration(IdentifierName("var")) // Cannot use Token(SyntaxKind.VarKeyword) here (throws an ArgumentException) - .AddVariables( - VariableDeclarator(Identifier("instance")) - .WithInitializer(EqualsValueClause( - CastExpression( - IdentifierName(classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), - IdentifierName("obj"))))))) - .AddStatements(EnumerateValidationStatements(classSymbol, validationSymbol, observablePropertySymbol).ToArray())), - ReturnStatement(IdentifierName("ValidateAllProperties"))))))) - .NormalizeWhitespace() - .ToFullString(); - - // Reset the attributes list (so the same class doesn't get duplicate attributes) - classAttributes = Array.Empty(); - - // Add the partial type - context.AddSource($"{classSymbol.GetFullMetadataNameForFileName()}.cs", SourceText.From(source, Encoding.UTF8)); - } - } - - /// - /// Gets a sequence of statements to validate declared properties (including generated ones). - /// - /// The input instance to process. - /// The type symbol for the ValidationAttribute type. - /// The type symbol for the ObservablePropertyAttribute type. - /// The sequence of instances to validate declared properties. - [Pure] - private static IEnumerable EnumerateValidationStatements(INamedTypeSymbol classSymbol, INamedTypeSymbol validationSymbol, INamedTypeSymbol observablePropertySymbol) - { - foreach (var memberSymbol in classSymbol.GetMembers()) - { - if (memberSymbol is not (IPropertySymbol { IsIndexer: false } or IFieldSymbol)) - { - continue; - } - - ImmutableArray attributes = memberSymbol.GetAttributes(); - - // Also include fields that are annotated with [ObservableProperty]. This is necessary because - // all generators run in an undefined order and looking at the same original compilation, so the - // current one wouldn't be able to see generated properties from other generators directly. - if (memberSymbol is IFieldSymbol && - !attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, observablePropertySymbol))) - { - continue; - } - - // Skip the current member if there are no validation attributes applied to it - if (!attributes.Any(a => a.AttributeClass?.InheritsFrom(validationSymbol) == true)) - { - continue; - } - - // Get the target property name either directly or matching the generated one - string propertyName = memberSymbol switch - { - IPropertySymbol propertySymbol => propertySymbol.Name, - IFieldSymbol fieldSymbol => ObservablePropertyGenerator.GetGeneratedPropertyName(fieldSymbol), - _ => throw new InvalidOperationException("Invalid symbol type") - }; - - // This enumerator produces a sequence of statements as follows: - // - // __ObservableValidatorHelper.ValidateProperty(instance, instance., nameof(instance.)); - // __ObservableValidatorHelper.ValidateProperty(instance, instance., nameof(instance.)); - // ... - // __ObservableValidatorHelper.ValidateProperty(instance, instance., nameof(instance.)); - yield return - ExpressionStatement( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("__ObservableValidatorHelper"), - IdentifierName("ValidateProperty"))) - .AddArgumentListArguments( - Argument(IdentifierName("instance")), - Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("instance"), - IdentifierName(propertyName))), - Argument( - InvocationExpression(IdentifierName("nameof")) - .AddArgumentListArguments(Argument( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("instance"), - IdentifierName(propertyName))))))); - } - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.SyntaxReceiver.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.SyntaxReceiver.cs deleted file mode 100644 index b370089d644..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.SyntaxReceiver.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - public abstract partial class TransitiveMembersGenerator - { - /// - /// An that selects candidate nodes to process. - /// - private sealed class SyntaxReceiver : ISyntaxContextReceiver - { - /// - /// The fully qualified name of the attribute type to look for. - /// - private readonly string attributeTypeFullName; - - /// - /// The list of info gathered during exploration. - /// - private readonly List gatheredInfo = new(); - - /// - /// Initializes a new instance of the class. - /// - /// The fully qualified name of the attribute type to look for. - public SyntaxReceiver(string attributeTypeFullName) - { - this.attributeTypeFullName = attributeTypeFullName; - } - - /// - /// Gets the collection of gathered info to process. - /// - public IReadOnlyCollection GatheredInfo => this.gatheredInfo; - - /// - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) - { - if (context.Node is ClassDeclarationSyntax { AttributeLists: { Count: > 0 } } classDeclaration && - context.SemanticModel.GetDeclaredSymbol(classDeclaration) is INamedTypeSymbol classSymbol && - context.SemanticModel.Compilation.GetTypeByMetadataName(this.attributeTypeFullName) is INamedTypeSymbol attributeSymbol && - classSymbol.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeSymbol)) is AttributeData attributeData && - attributeData.ApplicationSyntaxReference is SyntaxReference syntaxReference && - syntaxReference.GetSyntax() is AttributeSyntax attributeSyntax) - { - this.gatheredInfo.Add(new Item(classDeclaration, classSymbol, attributeSyntax, attributeData)); - } - } - - /// - /// A model for a group of item representing a discovered type to process. - /// - /// The instance for the target class declaration. - /// The instance for . - /// The instance for the target attribute over . - /// The instance for . - public sealed record Item( - ClassDeclarationSyntax ClassDeclaration, - INamedTypeSymbol ClassSymbol, - AttributeSyntax AttributeSyntax, - AttributeData AttributeData); - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs deleted file mode 100644 index 6c848347727..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/ComponentModel/TransitiveMembersGenerator.cs +++ /dev/null @@ -1,286 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using static Microsoft.CodeAnalysis.SymbolDisplayTypeQualificationStyle; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for a given attribute type. - /// - public abstract partial class TransitiveMembersGenerator : ISourceGenerator - { - /// - /// The fully qualified name of the attribute type to look for. - /// - private readonly string attributeTypeFullName; - - /// - /// The name of the attribute type to look for. - /// - private readonly string attributeTypeName; - - /// - /// Initializes a new instance of the class. - /// - /// The fully qualified name of the attribute type to look for. - protected TransitiveMembersGenerator(string attributeTypeFullName) - { - this.attributeTypeFullName = attributeTypeFullName; - this.attributeTypeName = attributeTypeFullName.Split('.').Last(); - } - - /// - /// Gets a indicating when the generation failed for a given type. - /// - protected abstract DiagnosticDescriptor TargetTypeErrorDescriptor { get; } - - /// - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(() => new SyntaxReceiver(this.attributeTypeFullName)); - } - - /// - public void Execute(GeneratorExecutionContext context) - { - // Get the syntax receiver with the candidate nodes - if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver || - syntaxReceiver.GatheredInfo.Count == 0) - { - return; - } - - // Validate the language version - if (context.ParseOptions is not CSharpParseOptions { LanguageVersion: >= LanguageVersion.CSharp9 }) - { - context.ReportDiagnostic(Diagnostic.Create(UnsupportedCSharpLanguageVersionError, null)); - } - - // Load the syntax tree with the members to generate - SyntaxTree sourceSyntaxTree = LoadSourceSyntaxTree(); - - foreach (SyntaxReceiver.Item item in syntaxReceiver.GatheredInfo) - { - if (!ValidateTargetType(context, item.AttributeData, item.ClassDeclaration, item.ClassSymbol, out var descriptor)) - { - context.ReportDiagnostic(descriptor, item.AttributeSyntax, item.ClassSymbol); - - continue; - } - - try - { - OnExecute(context, item.AttributeData, item.ClassDeclaration, item.ClassSymbol, sourceSyntaxTree); - } - catch - { - context.ReportDiagnostic(TargetTypeErrorDescriptor, item.AttributeSyntax, item.ClassSymbol); - } - } - } - - /// - /// Loads the source syntax tree for the current generator. - /// - /// The syntax tree with the elements to emit in the generated code. - [Pure] - private SyntaxTree LoadSourceSyntaxTree() - { - string filename = $"Microsoft.Toolkit.Mvvm.SourceGenerators.EmbeddedResources.{this.attributeTypeName.Replace("Attribute", string.Empty)}.cs"; - - Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(filename); - StreamReader reader = new(stream); - - string observableObjectSource = reader.ReadToEnd(); - - return CSharpSyntaxTree.ParseText(observableObjectSource); - } - - /// - /// Processes a given target type. - /// - /// The input instance to use. - /// The for the current attribute being processed. - /// The node to process. - /// The for . - /// The for the target parsed source. - private void OnExecute( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - SyntaxTree sourceSyntaxTree) - { - ClassDeclarationSyntax sourceDeclaration = sourceSyntaxTree.GetRoot().DescendantNodes().OfType().First(); - UsingDirectiveSyntax[] usingDirectives = sourceSyntaxTree.GetRoot().DescendantNodes().OfType().ToArray(); - BaseListSyntax? baseListSyntax = BaseList(SeparatedList( - sourceDeclaration.BaseList?.Types - .OfType() - .Select(static t => t.Type) - .OfType() - .Where(static t => t.Identifier.ValueText.StartsWith("I")) - .Select(static t => SimpleBaseType(t)) - .ToArray() - ?? Array.Empty())); - - if (baseListSyntax.Types.Count == 0) - { - baseListSyntax = null; - } - - // Create the class declaration for the user type. This will produce a tree as follows: - // - // : - // { - // - // } - var classDeclarationSyntax = - ClassDeclaration(classDeclaration.Identifier.Text) - .WithModifiers(classDeclaration.Modifiers) - .WithBaseList(baseListSyntax) - .AddMembers(OnLoadDeclaredMembers(context, attributeData, classDeclaration, classDeclarationSymbol, sourceDeclaration).ToArray()); - - TypeDeclarationSyntax typeDeclarationSyntax = classDeclarationSyntax; - - // Add all parent types in ascending order, if any - foreach (var parentType in classDeclaration.Ancestors().OfType()) - { - typeDeclarationSyntax = parentType - .WithMembers(SingletonList(typeDeclarationSyntax)) - .WithConstraintClauses(List()) - .WithBaseList(null) - .WithAttributeLists(List()) - .WithoutTrivia(); - } - - // Create the compilation unit with the namespace and target member. - // From this, we can finally generate the source code to output. - var namespaceName = classDeclarationSymbol.ContainingNamespace.ToDisplayString(new(typeQualificationStyle: NameAndContainingTypesAndNamespaces)); - - // Create the final compilation unit to generate (with using directives and the full type declaration) - var source = - CompilationUnit() - .AddMembers(NamespaceDeclaration(IdentifierName(namespaceName)) - .AddMembers(typeDeclarationSyntax)) - .AddUsings(usingDirectives.First().WithLeadingTrivia(TriviaList( - Comment("// Licensed to the .NET Foundation under one or more agreements."), - Comment("// The .NET Foundation licenses this file to you under the MIT license."), - Comment("// See the LICENSE file in the project root for more information."), - Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true))))) - .AddUsings(usingDirectives.Skip(1).ToArray()) - .NormalizeWhitespace() - .ToFullString(); - - // Add the partial type - context.AddSource($"{classDeclarationSymbol.GetFullMetadataNameForFileName()}.cs", SourceText.From(source, Encoding.UTF8)); - } - - /// - /// Loads the nodes to generate from the input parsed tree. - /// - /// The input instance to use. - /// The for the current attribute being processed. - /// The node to process. - /// The for . - /// The parsed instance with the source nodes. - /// A sequence of nodes to emit in the generated file. - private IEnumerable OnLoadDeclaredMembers( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - ClassDeclarationSyntax sourceDeclaration) - { - IEnumerable generatedMembers = FilterDeclaredMembers(context, attributeData, classDeclaration, classDeclarationSymbol, sourceDeclaration); - - // Add the attributes on each member - return generatedMembers.Select(member => - { - // [GeneratedCode] is always present - member = member - .WithoutLeadingTrivia() - .AddAttributeLists(AttributeList(SingletonSeparatedList( - Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")) - .AddArgumentListArguments( - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().Assembly.GetName().Version.ToString()))))))) - .WithLeadingTrivia(member.GetLeadingTrivia()); - - // [DebuggerNonUserCode] is not supported over interfaces, events or fields - if (member.Kind() is not SyntaxKind.InterfaceDeclaration and not SyntaxKind.EventFieldDeclaration and not SyntaxKind.FieldDeclaration) - { - member = member.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode"))))); - } - - // [ExcludeFromCodeCoverage] is not supported on interfaces and fields - if (member.Kind() is not SyntaxKind.InterfaceDeclaration and not SyntaxKind.FieldDeclaration) - { - member = member.AddAttributeLists(AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))))); - } - - // If the target class is sealed, make protected members private and remove the virtual modifier - if (classDeclarationSymbol.IsSealed) - { - return member - .ReplaceModifier(SyntaxKind.ProtectedKeyword, SyntaxKind.PrivateKeyword) - .RemoveModifier(SyntaxKind.VirtualKeyword); - } - - return member; - }); - } - - /// - /// Validates a target type being processed. - /// - /// The input instance to use. - /// The for the current attribute being processed. - /// The node to process. - /// The for . - /// The resulting to emit in case the target type isn't valid. - /// Whether or not the target type is valid and can be processed normally. - protected abstract bool ValidateTargetType( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - [NotNullWhen(false)] out DiagnosticDescriptor? descriptor); - - /// - /// Filters the nodes to generate from the input parsed tree. - /// - /// The input instance to use. - /// The for the current attribute being processed. - /// The node to process. - /// The for . - /// The parsed instance with the source nodes. - /// A sequence of nodes to emit in the generated file. - protected virtual IEnumerable FilterDeclaredMembers( - GeneratorExecutionContext context, - AttributeData attributeData, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - ClassDeclarationSyntax sourceDeclaration) - { - return sourceDeclaration.Members; - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs deleted file mode 100644 index 52ab2c33079..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticDescriptors.cs +++ /dev/null @@ -1,224 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics -{ - /// - /// A container for all instances for errors reported by analyzers in this project. - /// - internal static class DiagnosticDescriptors - { - /// - /// Gets a indicating when failed to run on a given type. - /// - /// Format: "The generator INotifyPropertyChangedGenerator failed to execute on type {0}". - /// - /// - public static readonly DiagnosticDescriptor INotifyPropertyChangedGeneratorError = new( - id: "MVVMTK0001", - title: $"Internal error for {nameof(INotifyPropertyChangedGenerator)}", - messageFormat: $"The generator {nameof(INotifyPropertyChangedGenerator)} failed to execute on type {{0}}", - category: typeof(INotifyPropertyChangedGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"The {nameof(INotifyPropertyChangedGenerator)} generator encountered an error while processing a type. Please report this issue at https://aka.ms/mvvmtoolkit.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when failed to run on a given type. - /// - /// Format: "The generator ObservableObjectGenerator failed to execute on type {0}". - /// - /// - public static readonly DiagnosticDescriptor ObservableObjectGeneratorError = new( - id: "MVVMTK0002", - title: $"Internal error for {nameof(ObservableObjectGenerator)}", - messageFormat: $"The generator {nameof(ObservableObjectGenerator)} failed to execute on type {{0}}", - category: typeof(ObservableObjectGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"The {nameof(ObservableObjectGenerator)} generator encountered an error while processing a type. Please report this issue at https://aka.ms/mvvmtoolkit.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when failed to run on a given type. - /// - /// Format: "The generator ObservableRecipientGenerator failed to execute on type {0}". - /// - /// - public static readonly DiagnosticDescriptor ObservableRecipientGeneratorError = new( - id: "MVVMTK0003", - title: $"Internal error for {nameof(ObservableRecipientGenerator)}", - messageFormat: $"The generator {nameof(ObservableRecipientGenerator)} failed to execute on type {{0}}", - category: typeof(ObservableRecipientGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"The {nameof(ObservableRecipientGenerator)} generator encountered an error while processing a type. Please report this issue at https://aka.ms/mvvmtoolkit.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when a duplicate declaration of would happen. - /// - /// Format: "Cannot apply [INotifyPropertyChangedAttribute] to type {0}, as it already declares the INotifyPropertyChanged interface". - /// - /// - public static readonly DiagnosticDescriptor DuplicateINotifyPropertyChangedInterfaceForINotifyPropertyChangedAttributeError = new( - id: "MVVMTK0004", - title: $"Duplicate {nameof(INotifyPropertyChanged)} definition", - messageFormat: $"Cannot apply [INotifyPropertyChanged] to type {{0}}, as it already declares the {nameof(INotifyPropertyChanged)} interface", - category: typeof(INotifyPropertyChangedGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot apply [INotifyPropertyChanged] to a type that already declares the {nameof(INotifyPropertyChanged)} interface.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when a duplicate declaration of would happen. - /// - /// Format: "Cannot apply [ObservableObjectAttribute] to type {0}, as it already declares the INotifyPropertyChanged interface". - /// - /// - public static readonly DiagnosticDescriptor DuplicateINotifyPropertyChangedInterfaceForObservableObjectAttributeError = new( - id: "MVVMTK0005", - title: $"Duplicate {nameof(INotifyPropertyChanged)} definition", - messageFormat: $"Cannot apply [ObservableObject] to type {{0}}, as it already declares the {nameof(INotifyPropertyChanged)} interface", - category: typeof(ObservableObjectGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot apply [ObservableObject] to a type that already declares the {nameof(INotifyPropertyChanged)} interface.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when a duplicate declaration of would happen. - /// - /// Format: "Cannot apply [ObservableObjectAttribute] to type {0}, as it already declares the INotifyPropertyChanging interface". - /// - /// - public static readonly DiagnosticDescriptor DuplicateINotifyPropertyChangingInterfaceForObservableObjectAttributeError = new( - id: "MVVMTK0006", - title: $"Duplicate {nameof(INotifyPropertyChanging)} definition", - messageFormat: $"Cannot apply [ObservableObject] to type {{0}}, as it already declares the {nameof(INotifyPropertyChanging)} interface", - category: typeof(ObservableObjectGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot apply [ObservableObject] to a type that already declares the {nameof(INotifyPropertyChanging)} interface.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when a duplicate declaration of would happen. - /// - /// Format: "Cannot apply [ObservableRecipientAttribute] to type {0}, as it already inherits from the ObservableRecipient class". - /// - /// - public static readonly DiagnosticDescriptor DuplicateObservableRecipientError = new( - id: "MVVMTK0007", - title: "Duplicate ObservableRecipient definition", - messageFormat: $"Cannot apply [ObservableRecipient] to type {{0}}, as it already inherits from the ObservableRecipient class", - category: typeof(ObservableRecipientGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot apply [ObservableRecipient] to a type that already inherits from the ObservableRecipient class.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when there is a missing base functionality to enable ObservableRecipientAttribute. - /// - /// Format: "Cannot apply [ObservableRecipientAttribute] to type {0}, as it lacks necessary base functionality (it should either inherit from ObservableObject, or be annotated with [ObservableObjectAttribute] or [INotifyPropertyChangedAttribute])". - /// - /// - public static readonly DiagnosticDescriptor MissingBaseObservableObjectFunctionalityError = new( - id: "MVVMTK0008", - title: "Missing base ObservableObject functionality", - messageFormat: $"Cannot apply [ObservableRecipient] to type {{0}}, as it lacks necessary base functionality (it should either inherit from ObservableObject, or be annotated with [ObservableObject] or [INotifyPropertyChanged])", - category: typeof(ObservableRecipientGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot apply [ObservableRecipient] to a type that lacks necessary base functionality (it should either inherit from ObservableObject, or be annotated with [ObservableObject] or [INotifyPropertyChanged]).", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when the target type doesn't inherit from the ObservableValidator class. - /// - /// Format: "The field {0}.{1} cannot be used to generate an observable property, as it has {2} validation attribute(s) but is declared in a type that doesn't inherit from ObservableValidator". - /// - /// - public static readonly DiagnosticDescriptor MissingObservableValidatorInheritanceError = new( - id: "MVVMTK0009", - title: "Missing ObservableValidator inheritance", - messageFormat: "The field {0}.{1} cannot be used to generate an observable property, as it has {2} validation attribute(s) but is declared in a type that doesn't inherit from ObservableValidator", - category: typeof(ObservablePropertyGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot apply [ObservableProperty] to fields with validation attributes if they are declared in a type that doesn't inherit from ObservableValidator.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when failed to run on a given type. - /// - /// Format: "The generator ObservablePropertyGenerator failed to execute on type {0}". - /// - /// - public static readonly DiagnosticDescriptor ObservablePropertyGeneratorError = new( - id: "MVVMTK0010", - title: $"Internal error for {nameof(ObservablePropertyGenerator)}", - messageFormat: $"The generator {nameof(ObservablePropertyGenerator)} failed to execute on type {{0}}", - category: typeof(ObservableObjectGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"The {nameof(ObservablePropertyGenerator)} generator encountered an error while processing a type. Please report this issue at https://aka.ms/mvvmtoolkit.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when failed to run on a given type. - /// - /// Format: "The generator ICommandGenerator failed to execute on type {0}". - /// - /// - public static readonly DiagnosticDescriptor ICommandGeneratorError = new( - id: "MVVMTK0011", - title: $"Internal error for {nameof(ICommandGenerator)}", - messageFormat: $"The generator {nameof(ICommandGenerator)} failed to execute on type {{0}}", - category: typeof(ICommandGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"The {nameof(ICommandGenerator)} generator encountered an error while processing a type. Please report this issue at https://aka.ms/mvvmtoolkit.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when an annotated method to generate a command for has an invalid signature. - /// - /// Format: "The method {0}.{1} cannot be used to generate a command property, as its signature isn't compatible with any of the existing relay command types". - /// - /// - public static readonly DiagnosticDescriptor InvalidICommandMethodSignatureError = new( - id: "MVVMTK0012", - title: "Invalid ICommand method signature", - messageFormat: "The method {0}.{1} cannot be used to generate a command property, as its signature isn't compatible with any of the existing relay command types", - category: typeof(ICommandGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: "Cannot apply [ICommand] to methods with a signature that doesn't match any of the existing relay command types.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - - /// - /// Gets a indicating when an unsupported C# language version is being used. - /// - /// Format: "The method {0}.{1} cannot be used to generate a command property, as its signature isn't compatible with any of the existing relay command types". - /// - /// - public static readonly DiagnosticDescriptor UnsupportedCSharpLanguageVersionError = new( - id: "MVVMTK0013", - title: "Unsupported C# language version", - messageFormat: "The source generator features from the MVVM Toolkit require consuming projects to set the C# language version to at least C# 9.0", - category: typeof(CSharpParseOptions).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: "The source generator features from the MVVM Toolkit require consuming projects to set the C# language version to at least C# 9.0. Make sure to add 9.0 (or above) to your .csproj file.", - helpLinkUri: "https://aka.ms/mvvmtoolkit"); - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticExtensions.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticExtensions.cs deleted file mode 100644 index 5e7e6830f88..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Diagnostics/DiagnosticExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Linq; -using Microsoft.CodeAnalysis; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics -{ - /// - /// Extension methods for , specifically for reporting diagnostics. - /// - internal static class DiagnosticExtensions - { - /// - /// Adds a new diagnostics to the current compilation. - /// - /// The instance currently in use. - /// The input for the diagnostics to create. - /// The source to attach the diagnostics to. - /// The optional arguments for the formatted message to include. - public static void ReportDiagnostic( - this GeneratorExecutionContext context, - DiagnosticDescriptor descriptor, - ISymbol symbol, - params object[] args) - { - context.ReportDiagnostic(Diagnostic.Create(descriptor, symbol.Locations.FirstOrDefault(), args)); - } - - /// - /// Adds a new diagnostics to the current compilation. - /// - /// The instance currently in use. - /// The input for the diagnostics to create. - /// The source to attach the diagnostics to. - /// The optional arguments for the formatted message to include. - public static void ReportDiagnostic( - this GeneratorExecutionContext context, - DiagnosticDescriptor descriptor, - SyntaxNode node, - params object[] args) - { - context.ReportDiagnostic(Diagnostic.Create(descriptor, node.GetLocation(), args)); - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs deleted file mode 100644 index 3f9a4a7c8e7..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/EmbeddedResources/INotifyPropertyChanged.cs +++ /dev/null @@ -1,497 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// A base class for objects implementing . - /// - public abstract class NotifyPropertyChanged : INotifyPropertyChanged - { - /// - public event PropertyChangedEventHandler? PropertyChanged; - - /// - /// Raises the event. - /// - /// The input instance. - protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) - { - PropertyChanged?.Invoke(this, e); - } - - /// - /// Raises the event. - /// - /// (optional) The name of the property that changed. - protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); - } - - /// - /// Compares the current and new values for a given property. If the value has changed, updates - /// the property with the new value, then raises the event. - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The event is not raised if the current and new value for the target property are the same. - /// - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null) - { - if (EqualityComparer.Default.Equals(field, newValue)) - { - return false; - } - - field = newValue; - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, updates - /// the property with the new value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer comparer, [CallerMemberName] string? propertyName = null) - { - if (comparer.Equals(field, newValue)) - { - return false; - } - - field = newValue; - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, updates - /// the property with the new value, then raises the event. - /// This overload is much less efficient than and it - /// should only be used when the former is not viable (eg. when the target property being - /// updated does not directly expose a backing field that can be passed by reference). - /// For performance reasons, it is recommended to use a stateful callback if possible through - /// the whenever possible - /// instead of this overload, as that will allow the C# compiler to cache the input callback and - /// reduce the memory allocations. More info on that overload are available in the related XML - /// docs. This overload is here for completeness and in cases where that is not applicable. - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The event is not raised if the current and new value for the target property are the same. - /// - protected bool SetProperty(T oldValue, T newValue, Action callback, [CallerMemberName] string? propertyName = null) - { - if (EqualityComparer.Default.Equals(oldValue, newValue)) - { - return false; - } - - callback(newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, updates - /// the property with the new value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, Action callback, [CallerMemberName] string? propertyName = null) - { - if (comparer.Equals(oldValue, newValue)) - { - return false; - } - - callback(newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// updates the property and then raises the event. - /// The behavior mirrors that of , - /// with the difference being that this method is used to relay properties from a wrapped model in the - /// current instance. This type is useful when creating wrapping, bindable objects that operate over - /// models that lack support for notification (eg. for CRUD operations). - /// Suppose we have this model (eg. for a database row in a table): - /// - /// public class Person - /// { - /// public string Name { get; set; } - /// } - /// - /// We can then use a property to wrap instances of this type into our observable model (which supports - /// notifications), injecting the notification to the properties of that model, like so: - /// - /// [INotifyPropertyChanged] - /// public partial class BindablePerson - /// { - /// public Model { get; } - /// - /// public BindablePerson(Person model) - /// { - /// Model = model; - /// } - /// - /// public string Name - /// { - /// get => Model.Name; - /// set => Set(Model.Name, value, Model, (model, name) => model.Name = name); - /// } - /// } - /// - /// This way we can then use the wrapping object in our application, and all those "proxy" properties will - /// also raise notifications when changed. Note that this method is not meant to be a replacement for - /// , and it should only be used when relaying properties to a model that - /// doesn't support notifications, and only if you can't implement notifications to that model directly (eg. by having - /// it inherit from ). The syntax relies on passing the target model and a stateless callback - /// to allow the C# compiler to cache the function, which results in much better performance and no memory usage. - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The model containing the property being updated. - /// The callback to invoke to set the target property value, if a change has occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The event is not raised if the current and new value for the target property are the same. - /// - protected bool SetProperty(T oldValue, T newValue, TModel model, Action callback, [CallerMemberName] string? propertyName = null) - where TModel : class - { - if (EqualityComparer.Default.Equals(oldValue, newValue)) - { - return false; - } - - callback(model, newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// updates the property and then raises the event. - /// The behavior mirrors that of , - /// with the difference being that this method is used to relay properties from a wrapped model in the - /// current instance. See additional notes about this overload in . - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// The model containing the property being updated. - /// The callback to invoke to set the target property value, if a change has occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, TModel model, Action callback, [CallerMemberName] string? propertyName = null) - where TModel : class - { - if (comparer.Equals(oldValue, newValue)) - { - return false; - } - - callback(model, newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given field (which should be the backing field for a property). - /// If the value has changed, updates the field and then raises the event. - /// The behavior mirrors that of , with the difference being that - /// this method will also monitor the new value of the property (a generic ) and will also - /// raise the again for the target property when it completes. - /// This can be used to update bindings observing that or any of its properties. - /// This method and its overload specifically rely on the type, which needs - /// to be used in the backing field for the target property. The field doesn't need to be - /// initialized, as this method will take care of doing that automatically. The - /// type also includes an implicit operator, so it can be assigned to any instance directly. - /// Here is a sample property declaration using this method: - /// - /// private TaskNotifier myTask; - /// - /// public Task MyTask - /// { - /// get => myTask; - /// private set => SetAndNotifyOnCompletion(ref myTask, value); - /// } - /// - /// - /// The field notifier to modify. - /// The property's value after the change occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The event is not raised if the current and new value for the target property are - /// the same. The return value being only indicates that the new value being assigned to - /// is different than the previous one, and it does not mean the new - /// instance passed as argument is in any particular state. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null) - { - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName); - } - - /// - /// Compares the current and new values for a given field (which should be the backing field for a property). - /// If the value has changed, updates the field and then raises the event. - /// This method is just like , - /// with the difference being an extra parameter with a callback being invoked - /// either immediately, if the new task has already completed or is , or upon completion. - /// - /// The field notifier to modify. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The event is not raised if the current and new value for the target property are the same. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action callback, [CallerMemberName] string? propertyName = null) - { - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName); - } - - /// - /// Compares the current and new values for a given field (which should be the backing field for a property). - /// If the value has changed, updates the field and then raises the event. - /// The behavior mirrors that of , with the difference being that - /// this method will also monitor the new value of the property (a generic ) and will also - /// raise the again for the target property when it completes. - /// This can be used to update bindings observing that or any of its properties. - /// This method and its overload specifically rely on the type, which needs - /// to be used in the backing field for the target property. The field doesn't need to be - /// initialized, as this method will take care of doing that automatically. The - /// type also includes an implicit operator, so it can be assigned to any instance directly. - /// Here is a sample property declaration using this method: - /// - /// private TaskNotifier<int> myTask; - /// - /// public Task<int> MyTask - /// { - /// get => myTask; - /// private set => SetAndNotifyOnCompletion(ref myTask, value); - /// } - /// - /// - /// The type of result for the to set and monitor. - /// The field notifier to modify. - /// The property's value after the change occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The event is not raised if the current and new value for the target property are - /// the same. The return value being only indicates that the new value being assigned to - /// is different than the previous one, and it does not mean the new - /// instance passed as argument is in any particular state. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null) - { - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName); - } - - /// - /// Compares the current and new values for a given field (which should be the backing field for a property). - /// If the value has changed, updates the field and then raises the event. - /// This method is just like , - /// with the difference being an extra parameter with a callback being invoked - /// either immediately, if the new task has already completed or is , or upon completion. - /// - /// The type of result for the to set and monitor. - /// The field notifier to modify. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The event is not raised if the current and new value for the target property are the same. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action?> callback, [CallerMemberName] string? propertyName = null) - { - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName); - } - - /// - /// Implements the notification logic for the related methods. - /// - /// The type of to set and monitor. - /// The field notifier. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - private bool SetPropertyAndNotifyOnCompletion(ITaskNotifier taskNotifier, TTask? newValue, Action callback, [CallerMemberName] string? propertyName = null) - where TTask : Task - { - if (ReferenceEquals(taskNotifier.Task, newValue)) - { - return false; - } - - bool isAlreadyCompletedOrNull = newValue?.IsCompleted ?? true; - - taskNotifier.Task = newValue; - - OnPropertyChanged(propertyName); - - if (isAlreadyCompletedOrNull) - { - callback(newValue); - - return true; - } - - async void MonitorTask() - { - try - { - await newValue!; - } - catch - { - } - - if (ReferenceEquals(taskNotifier.Task, newValue)) - { - OnPropertyChanged(propertyName); - } - - callback(newValue); - } - - MonitorTask(); - - return true; - } - - /// - /// An interface for task notifiers of a specified type. - /// - /// The type of value to store. - private interface ITaskNotifier - where TTask : Task - { - /// - /// Gets or sets the wrapped value. - /// - TTask? Task { get; set; } - } - - /// - /// A wrapping class that can hold a value. - /// - protected sealed class TaskNotifier : ITaskNotifier - { - /// - /// Initializes a new instance of the class. - /// - internal TaskNotifier() - { - } - - private Task? task; - - /// - Task? ITaskNotifier.Task - { - get => this.task; - set => this.task = value; - } - - /// - /// Unwraps the value stored in the current instance. - /// - /// The input instance. - public static implicit operator Task?(TaskNotifier? notifier) - { - return notifier?.task; - } - } - - /// - /// A wrapping class that can hold a value. - /// - /// The type of value for the wrapped instance. - protected sealed class TaskNotifier : ITaskNotifier> - { - /// - /// Initializes a new instance of the class. - /// - internal TaskNotifier() - { - } - - private Task? task; - - /// - Task? ITaskNotifier>.Task - { - get => this.task; - set => this.task = value; - } - - /// - /// Unwraps the value stored in the current instance. - /// - /// The input instance. - public static implicit operator Task?(TaskNotifier? notifier) - { - return notifier?.task; - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/AttributeDataExtensions.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/AttributeDataExtensions.cs deleted file mode 100644 index 93f9ac9f1d4..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/AttributeDataExtensions.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions -{ - /// - /// Extension methods for the type. - /// - internal static class AttributeDataExtensions - { - /// - /// Checks whether a given instance contains a specified named argument. - /// - /// The type of argument to check. - /// The target instance to check. - /// The name of the argument to check. - /// The expected value for the target named argument. - /// Whether or not contains an argument named with the expected value. - [Pure] - public static bool HasNamedArgument(this AttributeData attributeData, string name, T? value) - { - foreach (KeyValuePair properties in attributeData.NamedArguments) - { - if (properties.Key == name) - { - return - properties.Value.Value is T argumentValue && - EqualityComparer.Default.Equals(argumentValue, value); - } - } - - return false; - } - - /// - /// Creates an node that is equivalent to the input instance. - /// - /// The input instance to process. - /// An replicating the data in . - [Pure] - public static AttributeSyntax AsAttributeSyntax(this AttributeData attributeData) - { - IdentifierNameSyntax attributeType = IdentifierName(attributeData.AttributeClass!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); - AttributeArgumentSyntax[] arguments = - attributeData.ConstructorArguments - .Select(static arg => AttributeArgument(ToExpression(arg))).Concat( - attributeData.NamedArguments - .Select(static arg => - AttributeArgument(ToExpression(arg.Value)) - .WithNameEquals(NameEquals(IdentifierName(arg.Key))))).ToArray(); - - return Attribute(attributeType, AttributeArgumentList(SeparatedList(SeparatedList(arguments)))); - - static ExpressionSyntax ToExpression(TypedConstant arg) - { - if (arg.IsNull) - { - return LiteralExpression(SyntaxKind.NullLiteralExpression); - } - - if (arg.Kind == TypedConstantKind.Array) - { - string elementType = ((IArrayTypeSymbol)arg.Type!).ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - return - ArrayCreationExpression( - ArrayType(IdentifierName(elementType)) - .AddRankSpecifiers(ArrayRankSpecifier(SingletonSeparatedList(OmittedArraySizeExpression())))) - .WithInitializer(InitializerExpression(SyntaxKind.ArrayInitializerExpression) - .AddExpressions(arg.Values.Select(ToExpression).ToArray())); - } - - switch ((arg.Kind, arg.Value)) - { - case (TypedConstantKind.Primitive, string text): - return LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(text)); - case (TypedConstantKind.Primitive, bool flag) when flag: - return LiteralExpression(SyntaxKind.TrueLiteralExpression); - case (TypedConstantKind.Primitive, bool): - return LiteralExpression(SyntaxKind.FalseLiteralExpression); - case (TypedConstantKind.Primitive, object value): - return LiteralExpression(SyntaxKind.NumericLiteralExpression, value switch - { - byte b => Literal(b), - char c => Literal(c), - double d => Literal(d), - float f => Literal(f), - int i => Literal(i), - long l => Literal(l), - sbyte sb => Literal(sb), - short sh => Literal(sh), - uint ui => Literal(ui), - ulong ul => Literal(ul), - ushort ush => Literal(ush), - _ => throw new ArgumentException() - }); - case (TypedConstantKind.Type, ITypeSymbol type): - return TypeOfExpression(IdentifierName(type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))); - case (TypedConstantKind.Enum, object value): - return CastExpression( - IdentifierName(arg.Type!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), - LiteralExpression(SyntaxKind.NumericLiteralExpression, ParseToken(value.ToString()))); - default: throw new ArgumentException(); - } - } - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/INamedTypeSymbolExtensions.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/INamedTypeSymbolExtensions.cs deleted file mode 100644 index e04728d019c..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/INamedTypeSymbolExtensions.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using System.Text; -using Microsoft.CodeAnalysis; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions -{ - /// - /// Extension methods for the type. - /// - internal static class INamedTypeSymbolExtensions - { - /// - /// Gets the full metadata name for a given instance. - /// - /// The input instance. - /// The full metadata name for . - [Pure] - public static string GetFullMetadataName(this INamedTypeSymbol symbol) - { - static StringBuilder BuildFrom(ISymbol? symbol, StringBuilder builder) - { - return symbol switch - { - INamespaceSymbol ns when ns.IsGlobalNamespace => builder, - INamespaceSymbol ns when ns.ContainingNamespace is { IsGlobalNamespace: false } - => BuildFrom(ns.ContainingNamespace, builder.Insert(0, $".{ns.MetadataName}")), - ITypeSymbol ts when ts.ContainingType is ISymbol pt => BuildFrom(pt, builder.Insert(0, $"+{ts.MetadataName}")), - ITypeSymbol ts when ts.ContainingNamespace is ISymbol pn => BuildFrom(pn, builder.Insert(0, $".{ts.MetadataName}")), - ISymbol => BuildFrom(symbol.ContainingSymbol, builder.Insert(0, symbol.MetadataName)), - _ => builder - }; - } - - return BuildFrom(symbol, new StringBuilder(256)).ToString(); - } - - /// - /// Gets a valid filename for a given instance. - /// - /// The input instance. - /// The full metadata name for that is also a valid filename. - [Pure] - public static string GetFullMetadataNameForFileName(this INamedTypeSymbol symbol) - { - return symbol.GetFullMetadataName().Replace('`', '-').Replace('+', '.'); - } - - /// - /// Checks whether or not a given inherits from a specified type. - /// - /// The target instance to check. - /// The type symbol of the type to check for inheritance. - /// Whether or not inherits from . - [Pure] - public static bool InheritsFrom(this INamedTypeSymbol typeSymbol, INamedTypeSymbol targetTypeSymbol) - { - INamedTypeSymbol? baseType = typeSymbol.BaseType; - - while (baseType != null) - { - if (SymbolEqualityComparer.Default.Equals(baseType, targetTypeSymbol)) - { - return true; - } - - baseType = baseType.BaseType; - } - - return false; - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/MemberDeclarationSyntaxExtensions.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/MemberDeclarationSyntaxExtensions.cs deleted file mode 100644 index cdc63118306..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Extensions/MemberDeclarationSyntaxExtensions.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions -{ - /// - /// Extension methods for the type. - /// - internal static class MemberDeclarationSyntaxExtensions - { - /// - /// Replaces a specific modifier. - /// - /// The input instance. - /// The target modifier kind to replace. - /// The new modifier kind to add or replace. - /// A instance with the target modifier. - [Pure] - public static MemberDeclarationSyntax ReplaceModifier(this MemberDeclarationSyntax memberDeclaration, SyntaxKind oldKind, SyntaxKind newKind) - { - int index = memberDeclaration.Modifiers.IndexOf(oldKind); - - if (index != -1) - { - return memberDeclaration.WithModifiers(memberDeclaration.Modifiers.Replace(memberDeclaration.Modifiers[index], Token(newKind))); - } - - return memberDeclaration; - } - - /// - /// Removes a specific modifier. - /// - /// The input instance. - /// The modifier kind to remove. - /// A instance without the specified modifier. - [Pure] - public static MemberDeclarationSyntax RemoveModifier(this MemberDeclarationSyntax memberDeclaration, SyntaxKind kind) - { - int index = memberDeclaration.Modifiers.IndexOf(kind); - - if (index != -1) - { - return memberDeclaration.WithModifiers(memberDeclaration.Modifiers.RemoveAt(index)); - } - - return memberDeclaration; - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Input/ICommandGenerator.SyntaxReceiver.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Input/ICommandGenerator.SyntaxReceiver.cs deleted file mode 100644 index 5e365a90b97..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Input/ICommandGenerator.SyntaxReceiver.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - public sealed partial class ICommandGenerator - { - /// - /// An that selects candidate nodes to process. - /// - private sealed class SyntaxReceiver : ISyntaxContextReceiver - { - /// - /// The list of info gathered during exploration. - /// - private readonly List gatheredInfo = new(); - - /// - /// Gets the collection of gathered info to process. - /// - public IReadOnlyCollection GatheredInfo => this.gatheredInfo; - - /// - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) - { - if (context.Node is MethodDeclarationSyntax methodDeclaration && - context.SemanticModel.GetDeclaredSymbol(methodDeclaration) is IMethodSymbol methodSymbol && - context.SemanticModel.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.ICommandAttribute") is INamedTypeSymbol iCommandSymbol && - methodSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, iCommandSymbol))) - { - this.gatheredInfo.Add(new Item(methodDeclaration.GetLeadingTrivia(), methodSymbol)); - } - } - - /// - /// A model for a group of item representing a discovered type to process. - /// - /// The leading trivia for the field declaration. - /// The instance for the target method. - public sealed record Item(SyntaxTriviaList LeadingTrivia, IMethodSymbol MethodSymbol); - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Input/ICommandGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Input/ICommandGenerator.cs deleted file mode 100644 index 9095f93f36c..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Input/ICommandGenerator.cs +++ /dev/null @@ -1,346 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using static Microsoft.CodeAnalysis.SymbolDisplayTypeQualificationStyle; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for generating command properties from annotated methods. - /// - [Generator] - public sealed partial class ICommandGenerator : ISourceGenerator - { - /// - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(static () => new SyntaxReceiver()); - } - - /// - public void Execute(GeneratorExecutionContext context) - { - // Get the syntax receiver with the candidate nodes - if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver || - syntaxReceiver.GatheredInfo.Count == 0) - { - return; - } - - // Validate the language version - if (context.ParseOptions is not CSharpParseOptions { LanguageVersion: >= LanguageVersion.CSharp9 }) - { - context.ReportDiagnostic(Diagnostic.Create(UnsupportedCSharpLanguageVersionError, null)); - } - - foreach (var items in syntaxReceiver.GatheredInfo.GroupBy(static item => item.MethodSymbol.ContainingType, SymbolEqualityComparer.Default)) - { - if (items.Key.DeclaringSyntaxReferences.Length > 0 && - items.Key.DeclaringSyntaxReferences.First().GetSyntax() is ClassDeclarationSyntax classDeclaration) - { - try - { - OnExecute(context, classDeclaration, items.Key, items); - } - catch - { - context.ReportDiagnostic(ICommandGeneratorError, classDeclaration, items.Key); - } - } - } - } - - /// - /// Processes a given target type. - /// - /// The input instance to use. - /// The node to process. - /// The for . - /// The sequence of instances to process. - private static void OnExecute( - GeneratorExecutionContext context, - ClassDeclarationSyntax classDeclaration, - INamedTypeSymbol classDeclarationSymbol, - IEnumerable items) - { - // Create the class declaration for the user type. This will produce a tree as follows: - // - // - // { - // - // } - var classDeclarationSyntax = - ClassDeclaration(classDeclarationSymbol.Name) - .WithModifiers(classDeclaration.Modifiers) - .AddMembers(items.Select(item => CreateCommandMembers(context, item.LeadingTrivia, item.MethodSymbol)).SelectMany(static g => g).ToArray()); - - TypeDeclarationSyntax typeDeclarationSyntax = classDeclarationSyntax; - - // Add all parent types in ascending order, if any - foreach (var parentType in classDeclaration.Ancestors().OfType()) - { - typeDeclarationSyntax = parentType - .WithMembers(SingletonList(typeDeclarationSyntax)) - .WithConstraintClauses(List()) - .WithBaseList(null) - .WithAttributeLists(List()) - .WithoutTrivia(); - } - - // Create the compilation unit with the namespace and target member. - // From this, we can finally generate the source code to output. - var namespaceName = classDeclarationSymbol.ContainingNamespace.ToDisplayString(new(typeQualificationStyle: NameAndContainingTypesAndNamespaces)); - - // Create the final compilation unit to generate (with leading trivia) - var source = - CompilationUnit().AddMembers( - NamespaceDeclaration(IdentifierName(namespaceName)).WithLeadingTrivia(TriviaList( - Comment("// Licensed to the .NET Foundation under one or more agreements."), - Comment("// The .NET Foundation licenses this file to you under the MIT license."), - Comment("// See the LICENSE file in the project root for more information."), - Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)))) - .AddMembers(typeDeclarationSyntax)) - .NormalizeWhitespace() - .ToFullString(); - - // Add the partial type - context.AddSource($"{classDeclarationSymbol.GetFullMetadataNameForFileName()}.cs", SourceText.From(source, Encoding.UTF8)); - } - - /// - /// Creates the instances for a specified command. - /// - /// The input instance to use. - /// The leading trivia for the field to process. - /// The input instance to process. - /// The instances for the input command. - [Pure] - private static IEnumerable CreateCommandMembers(GeneratorExecutionContext context, SyntaxTriviaList leadingTrivia, IMethodSymbol methodSymbol) - { - // Get the command member names - var (fieldName, propertyName) = GetGeneratedFieldAndPropertyNames(context, methodSymbol); - - // Get the command type symbols - if (!TryMapCommandTypesFromMethod( - context, - methodSymbol, - out ITypeSymbol? commandInterfaceTypeSymbol, - out ITypeSymbol? commandClassTypeSymbol, - out ITypeSymbol? delegateTypeSymbol)) - { - context.ReportDiagnostic(InvalidICommandMethodSignatureError, methodSymbol, methodSymbol.ContainingType, methodSymbol); - - return Array.Empty(); - } - - // Construct the generated field as follows: - // - // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] - // private ? ; - FieldDeclarationSyntax fieldDeclaration = - FieldDeclaration( - VariableDeclaration(NullableType(IdentifierName(commandClassTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))) - .AddVariables(VariableDeclarator(Identifier(fieldName)))) - .AddModifiers(Token(SyntaxKind.PrivateKeyword)) - .AddAttributeLists(AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.CodeDom.Compiler.GeneratedCode")) - .AddArgumentListArguments( - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ICommandGenerator).FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ICommandGenerator).Assembly.GetName().Version.ToString()))))))); - - SyntaxTriviaList summaryTrivia = SyntaxTriviaList.Empty; - - // Parse the docs, if present - foreach (SyntaxTrivia trivia in leadingTrivia) - { - if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) || - trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)) - { - string text = trivia.ToString(); - - Match match = Regex.Match(text, @".*?<\/summary>", RegexOptions.Singleline); - - if (match.Success) - { - summaryTrivia = TriviaList(Comment($"/// {match.Value}")); - - break; - } - } - } - - // Construct the generated property as follows (the explicit delegate cast is needed to avoid overload resolution conflicts): - // - // - // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] - // [global::System.Diagnostics.DebuggerNonUserCode] - // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - // public => ??= new (new ()); - PropertyDeclarationSyntax propertyDeclaration = - PropertyDeclaration( - IdentifierName(commandInterfaceTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), - Identifier(propertyName)) - .AddModifiers(Token(SyntaxKind.PublicKeyword)) - .AddAttributeLists( - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.CodeDom.Compiler.GeneratedCode")) - .AddArgumentListArguments( - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ICommandGenerator).FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(typeof(ICommandGenerator).Assembly.GetName().Version.ToString())))))) - .WithOpenBracketToken(Token(summaryTrivia, SyntaxKind.OpenBracketToken, TriviaList())), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))))) - .WithExpressionBody( - ArrowExpressionClause( - AssignmentExpression( - SyntaxKind.CoalesceAssignmentExpression, - IdentifierName(fieldName), - ObjectCreationExpression(IdentifierName(commandClassTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))) - .AddArgumentListArguments(Argument( - ObjectCreationExpression(IdentifierName(delegateTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))) - .AddArgumentListArguments(Argument(IdentifierName(methodSymbol.Name)))))))) - .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); - - return new MemberDeclarationSyntax[] { fieldDeclaration, propertyDeclaration }; - } - - /// - /// Get the generated field and property names for the input method. - /// - /// The input instance to use. - /// The input instance to process. - /// The generated field and property names for . - [Pure] - private static (string FieldName, string PropertyName) GetGeneratedFieldAndPropertyNames(GeneratorExecutionContext context, IMethodSymbol methodSymbol) - { - string propertyName = methodSymbol.Name; - - if (SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")) && - methodSymbol.Name.EndsWith("Async")) - { - propertyName = propertyName.Substring(0, propertyName.Length - "Async".Length); - } - - propertyName += "Command"; - - string fieldName = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1)}"; - - return (fieldName, propertyName); - } - - /// - /// Gets the type symbols for the input method, if supported. - /// - /// The input instance to use. - /// The input instance to process. - /// The command interface type symbol. - /// The command class type symbol. - /// The delegate type symbol for the wrapped method. - /// Whether or not was valid and the requested types have been set. - private static bool TryMapCommandTypesFromMethod( - GeneratorExecutionContext context, - IMethodSymbol methodSymbol, - [NotNullWhen(true)] out ITypeSymbol? commandInterfaceTypeSymbol, - [NotNullWhen(true)] out ITypeSymbol? commandClassTypeSymbol, - [NotNullWhen(true)] out ITypeSymbol? delegateTypeSymbol) - { - // Map to IRelayCommand, RelayCommand, Action - if (methodSymbol.ReturnsVoid && methodSymbol.Parameters.Length == 0) - { - commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IRelayCommand")!; - commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.RelayCommand")!; - delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Action")!; - - return true; - } - - // Map to IRelayCommand, RelayCommand, Action - if (methodSymbol.ReturnsVoid && - methodSymbol.Parameters.Length == 1 && - methodSymbol.Parameters[0] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } parameter) - { - commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IRelayCommand`1")!.Construct(parameter.Type); - commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.RelayCommand`1")!.Construct(parameter.Type); - delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Action`1")!.Construct(parameter.Type); - - return true; - } - - if (SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!)) - { - // Map to IAsyncRelayCommand, AsyncRelayCommand, Func - if (methodSymbol.Parameters.Length == 0) - { - commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand")!; - commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand")!; - delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`1")!.Construct(context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); - - return true; - } - - if (methodSymbol.Parameters.Length == 1 && - methodSymbol.Parameters[0] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } singleParameter) - { - // Map to IAsyncRelayCommand, AsyncRelayCommand, Func - if (SymbolEqualityComparer.Default.Equals(singleParameter.Type, context.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken")!)) - { - commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand")!; - commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand")!; - delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`2")!.Construct( - context.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken")!, - context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); - - return true; - } - - // Map to IAsyncRelayCommand, AsyncRelayCommand, Func - commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand`1")!.Construct(singleParameter.Type); - commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand`1")!.Construct(singleParameter.Type); - delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`2")!.Construct( - singleParameter.Type, - context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); - - return true; - } - - // Map to IAsyncRelayCommand, AsyncRelayCommand, Func - if (methodSymbol.Parameters.Length == 2 && - methodSymbol.Parameters[0] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } firstParameter && - methodSymbol.Parameters[1] is IParameterSymbol { RefKind: RefKind.None, Type: { IsRefLikeType: false, TypeKind: not TypeKind.Pointer and not TypeKind.FunctionPointer } } secondParameter && - SymbolEqualityComparer.Default.Equals(secondParameter.Type, context.Compilation.GetTypeByMetadataName("System.Threading.CancellationToken")!)) - { - commandInterfaceTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.IAsyncRelayCommand`1")!.Construct(firstParameter.Type); - commandClassTypeSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Input.AsyncRelayCommand`1")!.Construct(firstParameter.Type); - delegateTypeSymbol = context.Compilation.GetTypeByMetadataName("System.Func`3")!.Construct( - firstParameter.Type, - secondParameter.Type, - context.Compilation.GetTypeByMetadataName("System.Threading.Tasks.Task")!); - - return true; - } - } - - commandInterfaceTypeSymbol = null; - commandClassTypeSymbol = null; - delegateTypeSymbol = null; - - return false; - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.SyntaxReceiver.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.SyntaxReceiver.cs deleted file mode 100644 index 979cfb8f492..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.SyntaxReceiver.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - public sealed partial class IMessengerRegisterAllGenerator - { - /// - /// An that selects candidate nodes to process. - /// - private sealed class SyntaxReceiver : ISyntaxContextReceiver - { - /// - /// The list of info gathered during exploration. - /// - private readonly List gatheredInfo = new(); - - /// - /// Gets the collection of gathered info to process. - /// - public IReadOnlyCollection GatheredInfo => this.gatheredInfo; - - /// - public void OnVisitSyntaxNode(GeneratorSyntaxContext context) - { - if (context.Node is ClassDeclarationSyntax classDeclaration && - context.SemanticModel.GetDeclaredSymbol(classDeclaration) is INamedTypeSymbol { IsGenericType: false } classSymbol && - context.SemanticModel.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Messaging.IRecipient`1") is INamedTypeSymbol iRecipientSymbol && - classSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i.OriginalDefinition, iRecipientSymbol))) - { - this.gatheredInfo.Add(classSymbol); - } - } - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs deleted file mode 100644 index 10eb7f1a865..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Messaging/IMessengerRegisterAllGenerator.cs +++ /dev/null @@ -1,292 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; -using Microsoft.Toolkit.Mvvm.SourceGenerators.Extensions; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; -using static Microsoft.Toolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors; - -namespace Microsoft.Toolkit.Mvvm.SourceGenerators -{ - /// - /// A source generator for message registration without relying on compiled LINQ expressions. - /// - [Generator] - public sealed partial class IMessengerRegisterAllGenerator : ISourceGenerator - { - /// - public void Initialize(GeneratorInitializationContext context) - { - context.RegisterForSyntaxNotifications(static () => new SyntaxReceiver()); - } - - /// - public void Execute(GeneratorExecutionContext context) - { - // Get the syntax receiver with the candidate nodes - if (context.SyntaxContextReceiver is not SyntaxReceiver syntaxReceiver || - syntaxReceiver.GatheredInfo.Count == 0) - { - return; - } - - // Like in the ObservableValidator.ValidateALlProperties generator, execution is skipped if C# >= 8.0 isn't available - if (context.ParseOptions is not CSharpParseOptions { LanguageVersion: >= LanguageVersion.CSharp8 }) - { - return; - } - - // Get the symbol for the IRecipient interface type - INamedTypeSymbol iRecipientSymbol = context.Compilation.GetTypeByMetadataName("Microsoft.Toolkit.Mvvm.Messaging.IRecipient`1")!; - - // Prepare the attributes to add to the first class declaration - AttributeListSyntax[] classAttributes = new[] - { - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName($"global::System.CodeDom.Compiler.GeneratedCode")) - .AddArgumentListArguments( - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().FullName))), - AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(GetType().Assembly.GetName().Version.ToString())))))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode")))), - AttributeList(SingletonSeparatedList(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal("This type is not intended to be used directly by user code")))))) - }; - - foreach (INamedTypeSymbol classSymbol in syntaxReceiver.GatheredInfo) - { - // Create a static factory method to register all messages for a given recipient type. - // This follows the same pattern used in ObservableValidatorValidateAllPropertiesGenerator, - // with the same advantages mentioned there (type safety, more AOT-friendly, etc.). - // There are two versions that are generated: a non-generic one doing the registration - // with no tokens, which is the most common scenario and will help particularly in AOT - // scenarios, and a generic version that will support all other cases with custom tokens. - // Note: the generic overload has a different name to simplify the lookup with reflection. - // This code takes a class symbol and produces a compilation unit as follows: - // - // // Licensed to the .NET Foundation under one or more agreements. - // // The .NET Foundation licenses this file to you under the MIT license. - // // See the LICENSE file in the project root for more information. - // - // #pragma warning disable - // - // namespace Microsoft.Toolkit.Mvvm.Messaging.__Internals - // { - // [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] - // [global::System.Diagnostics.DebuggerNonUserCode] - // [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] - // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - // [global::System.Obsolete("This type is not intended to be used directly by user code")] - // internal static partial class __IMessengerExtensions - // { - // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - // [global::System.Obsolete("This method is not intended to be called directly by user code")] - // public static global::System.Action CreateAllMessagesRegistrator( _) - // { - // static void RegisterAll(IMessenger messenger, object obj) - // { - // var recipient = ()obj; - // - // } - // - // return RegisterAll; - // } - // - // [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - // [global::System.Obsolete("This method is not intended to be called directly by user code")] - // public static global::System.Action CreateAllMessagesRegistratorWithToken( _) - // where TToken : global::System.IEquatable - // { - // static void RegisterAll(IMessenger messenger, object obj, TToken token) - // { - // var recipient = ()obj; - // - // } - // - // return RegisterAll; - // } - // } - // } - var source = - CompilationUnit().AddMembers( - NamespaceDeclaration(IdentifierName("Microsoft.Toolkit.Mvvm.Messaging.__Internals")).WithLeadingTrivia(TriviaList( - Comment("// Licensed to the .NET Foundation under one or more agreements."), - Comment("// The .NET Foundation licenses this file to you under the MIT license."), - Comment("// See the LICENSE file in the project root for more information."), - Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)))).AddMembers( - ClassDeclaration("__IMessengerExtensions").AddModifiers( - Token(SyntaxKind.InternalKeyword), - Token(SyntaxKind.StaticKeyword), - Token(SyntaxKind.PartialKeyword)).AddAttributeLists(classAttributes).AddMembers( - MethodDeclaration( - GenericName("global::System.Action").AddTypeArgumentListArguments( - IdentifierName("IMessenger"), - PredefinedType(Token(SyntaxKind.ObjectKeyword))), - Identifier("CreateAllMessagesRegistrator")).AddAttributeLists( - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal("This method is not intended to be called directly by user code"))))))).AddModifiers( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.StaticKeyword)).AddParameterListParameters( - Parameter(Identifier("_")).WithType(IdentifierName(classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))) - .WithBody(Block( - LocalFunctionStatement( - PredefinedType(Token(SyntaxKind.VoidKeyword)), - Identifier("RegisterAll")) - .AddModifiers(Token(SyntaxKind.StaticKeyword)) - .AddParameterListParameters( - Parameter(Identifier("messenger")).WithType(IdentifierName("IMessenger")), - Parameter(Identifier("obj")).WithType(PredefinedType(Token(SyntaxKind.ObjectKeyword)))) - .WithBody(Block( - LocalDeclarationStatement( - VariableDeclaration(IdentifierName("var")) - .AddVariables( - VariableDeclarator(Identifier("recipient")) - .WithInitializer(EqualsValueClause( - CastExpression( - IdentifierName(classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), - IdentifierName("obj"))))))) - .AddStatements(EnumerateRegistrationStatements(classSymbol, iRecipientSymbol).ToArray())), - ReturnStatement(IdentifierName("RegisterAll")))), - MethodDeclaration( - GenericName("global::System.Action").AddTypeArgumentListArguments( - IdentifierName("IMessenger"), - PredefinedType(Token(SyntaxKind.ObjectKeyword)), - IdentifierName("TToken")), - Identifier("CreateAllMessagesRegistratorWithToken")).AddAttributeLists( - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.ComponentModel.EditorBrowsable")).AddArgumentListArguments( - AttributeArgument(ParseExpression("global::System.ComponentModel.EditorBrowsableState.Never"))))), - AttributeList(SingletonSeparatedList( - Attribute(IdentifierName("global::System.Obsolete")).AddArgumentListArguments( - AttributeArgument(LiteralExpression( - SyntaxKind.StringLiteralExpression, - Literal("This method is not intended to be called directly by user code"))))))).AddModifiers( - Token(SyntaxKind.PublicKeyword), - Token(SyntaxKind.StaticKeyword)).AddParameterListParameters( - Parameter(Identifier("_")).WithType(IdentifierName(classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))) - .AddTypeParameterListParameters(TypeParameter("TToken")) - .AddConstraintClauses( - TypeParameterConstraintClause("TToken") - .AddConstraints(TypeConstraint(GenericName("global::System.IEquatable").AddTypeArgumentListArguments(IdentifierName("TToken"))))) - .WithBody(Block( - LocalFunctionStatement( - PredefinedType(Token(SyntaxKind.VoidKeyword)), - Identifier("RegisterAll")) - .AddModifiers(Token(SyntaxKind.StaticKeyword)) - .AddParameterListParameters( - Parameter(Identifier("messenger")).WithType(IdentifierName("IMessenger")), - Parameter(Identifier("obj")).WithType(PredefinedType(Token(SyntaxKind.ObjectKeyword))), - Parameter(Identifier("token")).WithType(IdentifierName("TToken"))) - .WithBody(Block( - LocalDeclarationStatement( - VariableDeclaration(IdentifierName("var")) - .AddVariables( - VariableDeclarator(Identifier("recipient")) - .WithInitializer(EqualsValueClause( - CastExpression( - IdentifierName(classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), - IdentifierName("obj"))))))) - .AddStatements(EnumerateRegistrationStatementsWithTokens(classSymbol, iRecipientSymbol).ToArray())), - ReturnStatement(IdentifierName("RegisterAll"))))))) - .NormalizeWhitespace() - .ToFullString(); - - // Reset the attributes list (so the same class doesn't get duplicate attributes) - classAttributes = Array.Empty(); - - // Add the partial type - context.AddSource($"{classSymbol.GetFullMetadataNameForFileName()}.cs", SourceText.From(source, Encoding.UTF8)); - } - } - - /// - /// Gets a sequence of statements to register declared message handlers. - /// - /// The input instance to process. - /// The type symbol for the IRecipient<T> interface. - /// The sequence of instances to register message handleers. - [Pure] - private static IEnumerable EnumerateRegistrationStatements(INamedTypeSymbol classSymbol, INamedTypeSymbol iRecipientSymbol) - { - foreach (var interfaceSymbol in classSymbol.AllInterfaces) - { - if (!SymbolEqualityComparer.Default.Equals(interfaceSymbol.OriginalDefinition, iRecipientSymbol)) - { - continue; - } - - // This enumerator produces a sequence of statements as follows: - // - // messenger.Register<>(recipient); - // messenger.Register<>(recipient); - // ... - // messenger.Register<>(recipient); - yield return - ExpressionStatement( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("messenger"), - GenericName(Identifier("Register")).AddTypeArgumentListArguments( - IdentifierName(interfaceSymbol.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))))) - .AddArgumentListArguments(Argument(IdentifierName("recipient")))); - } - } - - /// - /// Gets a sequence of statements to register declared message handlers with custom tokens. - /// - /// The input instance to process. - /// The type symbol for the IRecipient<T> interface. - /// The sequence of instances to register message handleers. - [Pure] - private static IEnumerable EnumerateRegistrationStatementsWithTokens(INamedTypeSymbol classSymbol, INamedTypeSymbol iRecipientSymbol) - { - foreach (var interfaceSymbol in classSymbol.AllInterfaces) - { - if (!SymbolEqualityComparer.Default.Equals(interfaceSymbol.OriginalDefinition, iRecipientSymbol)) - { - continue; - } - - // This enumerator produces a sequence of statements as follows: - // - // messenger.Register<, TToken>(recipient, token); - // messenger.Register<, TToken>(recipient, token); - // ... - // messenger.Register<, TToken>(recipient, token); - yield return - ExpressionStatement( - InvocationExpression( - MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - IdentifierName("messenger"), - GenericName(Identifier("Register")).AddTypeArgumentListArguments( - IdentifierName(interfaceSymbol.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)), - IdentifierName("TToken")))) - .AddArgumentListArguments(Argument(IdentifierName("recipient")), Argument(IdentifierName("token")))); - } - } - } -} diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj b/Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj deleted file mode 100644 index fa2804b3282..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/Microsoft.Toolkit.Mvvm.SourceGenerators.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - netstandard2.0 - 9.0 - enable - false - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - - - - - - - - - diff --git a/Microsoft.Toolkit.Mvvm.SourceGenerators/System.Runtime.CompilerServices/IsExternalInit.cs b/Microsoft.Toolkit.Mvvm.SourceGenerators/System.Runtime.CompilerServices/IsExternalInit.cs deleted file mode 100644 index cadcf5a8570..00000000000 --- a/Microsoft.Toolkit.Mvvm.SourceGenerators/System.Runtime.CompilerServices/IsExternalInit.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel; - -namespace System.Runtime.CompilerServices -{ - /// - /// Reserved to be used by the compiler for tracking metadata. - /// This class should not be used by developers in source code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal static class IsExternalInit - { - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Attributes/NotNullAttribute.cs b/Microsoft.Toolkit.Mvvm/Attributes/NotNullAttribute.cs deleted file mode 100644 index 7725aeeeaf4..00000000000 --- a/Microsoft.Toolkit.Mvvm/Attributes/NotNullAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if NETSTANDARD2_0 - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that an output will not be null even if the corresponding type allows it. - /// Specifies that an input argument was not null when the call returns. - /// - /// Internal copy from the BCL attribute. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class NotNullAttribute : Attribute - { - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Attributes/NotNullIfNotNullAttribute.cs b/Microsoft.Toolkit.Mvvm/Attributes/NotNullIfNotNullAttribute.cs deleted file mode 100644 index df828c4deec..00000000000 --- a/Microsoft.Toolkit.Mvvm/Attributes/NotNullIfNotNullAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if NETSTANDARD2_0 - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that the output will be non-null if the named parameter is non-null. - /// - /// Internal copy from the BCL attribute. - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Attributes/NotNullWhenAttribute.cs b/Microsoft.Toolkit.Mvvm/Attributes/NotNullWhenAttribute.cs deleted file mode 100644 index 8b69958d2ca..00000000000 --- a/Microsoft.Toolkit.Mvvm/Attributes/NotNullWhenAttribute.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if NETSTANDARD2_0 - -namespace System.Diagnostics.CodeAnalysis -{ - /// - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - /// - /// Internal copy from the BCL attribute. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// - /// Gets a value indicating whether the annotated variable is not . - /// - public bool ReturnValue { get; } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/AlsoNotifyChangeForAttribute.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/AlsoNotifyChangeForAttribute.cs deleted file mode 100644 index 0eb967a4c32..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/AlsoNotifyChangeForAttribute.cs +++ /dev/null @@ -1,100 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Linq; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// An attribute that can be used to support in generated properties. When this attribute is - /// used, the generated property setter will also call (or the equivalent - /// method in the target class) for the properties specified in the attribute data. This can be useful to keep the code compact when - /// there are one or more dependent properties that should also be reported as updated when the value of the annotated observable - /// property is changed. If this attribute is used in a field without , it is ignored. - /// - /// In order to use this attribute, the containing type has to implement the interface - /// and expose a method with the same signature as . If the containing - /// type also implements the interface and exposes a method with the same signature as - /// , then this method will be invoked as well by the property setter. - /// - /// - /// This attribute can be used as follows: - /// - /// partial class MyViewModel : ObservableObject - /// { - /// [ObservableProperty] - /// [AlsoNotifyChangeFor(nameof(FullName))] - /// private string name; - /// - /// [ObservableProperty] - /// [AlsoNotifyChangeFor(nameof(FullName))] - /// private string surname; - /// - /// public string FullName => $"{Name} {Surname}"; - /// } - /// - /// - /// And with this, code analogous to this will be generated: - /// - /// partial class MyViewModel - /// { - /// public string Name - /// { - /// get => name; - /// set - /// { - /// if (SetProperty(ref name, value)) - /// { - /// OnPropertyChanged(nameof(FullName)); - /// } - /// } - /// } - /// - /// public string Surname - /// { - /// get => surname; - /// set - /// { - /// if (SetProperty(ref surname, value)) - /// { - /// OnPropertyChanged(nameof(FullName)); - /// } - /// } - /// } - /// } - /// - /// - [AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = false)] - public sealed class AlsoNotifyChangeForAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The name of the property to also notify when the annotated property changes. - public AlsoNotifyChangeForAttribute(string propertyName) - { - PropertyNames = new[] { propertyName }; - } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the property to also notify when the annotated property changes. - /// - /// The other property names to also notify when the annotated property changes. This parameter can optionally - /// be used to indicate a series of dependent properties from the same attribute, to keep the code more compact. - /// - public AlsoNotifyChangeForAttribute(string propertyName, params string[] otherPropertyNames) - { - PropertyNames = new[] { propertyName }.Concat(otherPropertyNames).ToArray(); - } - - /// - /// Gets the property names to also notify when the annotated property changes. - /// - public string[] PropertyNames { get; } - } -} diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/INotifyPropertyChangedAttribute.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/INotifyPropertyChangedAttribute.cs deleted file mode 100644 index eef91ae0ef8..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/INotifyPropertyChangedAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// An attribute that indicates that a given type should implement the interface and - /// have minimal built-in functionality to support it. This includes exposing the necessary event and having two methods - /// to raise it that mirror and - /// . For more extensive support, use . - /// - /// This attribute can be used as follows: - /// - /// [INotifyPropertyChanged] - /// partial class MyViewModel : SomeOtherClass - /// { - /// // Other members here... - /// } - /// - /// - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class INotifyPropertyChangedAttribute : Attribute - { - /// - /// Gets or sets a value indicating whether or not to also generate all the additional helper methods that are found - /// in as well (eg. . - /// If set to , only the event and - /// the two overloads will be generated. - /// The default value is . - /// - public bool IncludeAdditionalHelperMethods { get; set; } = true; - } -} diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservableObjectAttribute.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservableObjectAttribute.cs deleted file mode 100644 index 72c4b078c85..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservableObjectAttribute.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// An attribute that indicates that a given type should have all the members from - /// generated into it, as well as the and - /// interfaces. This can be useful when you want the same functionality from into a class - /// that already inherits from another one (since C# doesn't support multiple inheritance). This attribute will trigger - /// the source generator to just create the same APIs directly into the decorated class. - /// - /// This attribute can be used as follows: - /// - /// [ObservableObject] - /// partial class MyViewModel : SomeOtherClass - /// { - /// // Other members here... - /// } - /// - /// - /// And with this, the same APIs from will be available on this type as well. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ObservableObjectAttribute : Attribute - { - } -} diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservablePropertyAttribute.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservablePropertyAttribute.cs deleted file mode 100644 index 2395ec31ad9..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservablePropertyAttribute.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// An attribute that indicates that a given field should be wrapped by a generated observable property. - /// In order to use this attribute, the containing type has to implement the interface - /// and expose a method with the same signature as . If the containing - /// type also implements the interface and exposes a method with the same signature as - /// , then this method will be invoked as well by the property setter. - /// - /// This attribute can be used as follows: - /// - /// partial class MyViewModel : ObservableObject - /// { - /// [ObservableProperty] - /// private string name; - /// - /// [ObservableProperty] - /// private bool isEnabled; - /// } - /// - /// - /// And with this, code analogous to this will be generated: - /// - /// partial class MyViewModel - /// { - /// public string Name - /// { - /// get => name; - /// set => SetProperty(ref name, value); - /// } - /// - /// public bool IsEnabled - /// { - /// get => name; - /// set => SetProperty(ref isEnabled, value); - /// } - /// } - /// - /// - /// - /// The generated properties will automatically use the UpperCamelCase format for their names, - /// which will be derived from the field names. The generator can also recognize fields using either - /// the _lowerCamel or m_lowerCamel naming scheme. Otherwise, the first character in the - /// source field name will be converted to uppercase (eg. isEnabled to IsEnabled). - /// - [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)] - public sealed class ObservablePropertyAttribute : Attribute - { - } -} diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservableRecipientAttribute.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservableRecipientAttribute.cs deleted file mode 100644 index 382cce5c7e1..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/Attributes/ObservableRecipientAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// An attribute that indicates that a given type should have all the members from - /// generated into it. This can be useful when you want the same functionality from into - /// a class that already inherits from another one (since C# doesn't support multiple inheritance). This attribute will trigger - /// the source generator to just create the same APIs directly into the decorated class. For instance, this attribute can be - /// used to easily combine the functionality from both and , - /// by using as the base class and adding this attribute to the declared type. - /// - /// This attribute can be used as follows: - /// - /// [ObservableRecipient] - /// partial class MyViewModel : ObservableValidator - /// { - /// // Other members here... - /// } - /// - /// - /// And with this, the same APIs from will be available on this type as well. - /// - /// To avoid conflicts with other APIs in types where the new members are being generated, constructors are only generated when the annotated - /// type doesn't have any explicit constructors being declared. If that is the case, the same constructors from - /// are emitted, with the accessibility adapted to that of the annotated type. Otherwise, they are skipped, so the type being annotated has the - /// respondibility of properly initializing the property. Additionally, if the annotated type inherits - /// from , the overloads will be skipped - /// as well, as they would conflict with the methods. - /// - /// - /// - /// In order to work, needs to be applied to a type that inherits from - /// (either directly or indirectly), or to one decorated with . - /// This is because the methods rely on some of the inherited members to work. - /// If this condition is not met, the code will fail to build. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ObservableRecipientAttribute : Attribute - { - } -} diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableObject.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableObject.cs deleted file mode 100644 index 41fa934cfd6..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableObject.cs +++ /dev/null @@ -1,590 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1512 - -// This file is inspired from the MvvmLight library (lbugnion/MvvmLight), -// more info in ThirdPartyNotices.txt in the root of the project. - -// ================================== NOTE ================================== -// This file is mirrored in the trimmed-down INotifyPropertyChanged file in -// the source generator project, to be used with the [INotifyPropertyChanged], -// attribute, along with the ObservableObject annotated copy (for debugging info). -// If any changes are made to this file, they should also be appropriately -// ported to that file as well to keep the behavior consistent. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// A base class for objects of which the properties must be observable. - /// - public abstract class ObservableObject : INotifyPropertyChanged, INotifyPropertyChanging - { - /// - public event PropertyChangedEventHandler? PropertyChanged; - - /// - public event PropertyChangingEventHandler? PropertyChanging; - - /// - /// Raises the event. - /// - /// The input instance. - protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) - { - PropertyChanged?.Invoke(this, e); - } - - /// - /// Raises the event. - /// - /// The input instance. - protected virtual void OnPropertyChanging(PropertyChangingEventArgs e) - { - PropertyChanging?.Invoke(this, e); - } - - /// - /// Raises the event. - /// - /// (optional) The name of the property that changed. - protected void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); - } - - /// - /// Raises the event. - /// - /// (optional) The name of the property that changed. - protected void OnPropertyChanging([CallerMemberName] string? propertyName = null) - { - OnPropertyChanging(new PropertyChangingEventArgs(propertyName)); - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with the new - /// value, then raises the event. - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The and events are not raised - /// if the current and new value for the target property are the same. - /// - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, [CallerMemberName] string? propertyName = null) - { - // We duplicate the code here instead of calling the overload because we can't - // guarantee that the invoked SetProperty will be inlined, and we need the JIT - // to be able to see the full EqualityComparer.Default.Equals call, so that - // it'll use the intrinsics version of it and just replace the whole invocation - // with a direct comparison when possible (eg. for primitive numeric types). - // This is the fastest SetProperty overload so we particularly care about - // the codegen quality here, and the code is small and simple enough so that - // duplicating it still doesn't make the whole class harder to maintain. - if (EqualityComparer.Default.Equals(field, newValue)) - { - return false; - } - - OnPropertyChanging(propertyName); - - field = newValue; - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with the new - /// value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer comparer, [CallerMemberName] string? propertyName = null) - { - if (comparer.Equals(field, newValue)) - { - return false; - } - - OnPropertyChanging(propertyName); - - field = newValue; - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with the new - /// value, then raises the event. - /// This overload is much less efficient than and it - /// should only be used when the former is not viable (eg. when the target property being - /// updated does not directly expose a backing field that can be passed by reference). - /// For performance reasons, it is recommended to use a stateful callback if possible through - /// the whenever possible - /// instead of this overload, as that will allow the C# compiler to cache the input callback and - /// reduce the memory allocations. More info on that overload are available in the related XML - /// docs. This overload is here for completeness and in cases where that is not applicable. - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The and events are not raised - /// if the current and new value for the target property are the same. - /// - protected bool SetProperty(T oldValue, T newValue, Action callback, [CallerMemberName] string? propertyName = null) - { - // We avoid calling the overload again to ensure the comparison is inlined - if (EqualityComparer.Default.Equals(oldValue, newValue)) - { - return false; - } - - OnPropertyChanging(propertyName); - - callback(newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with the new - /// value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, Action callback, [CallerMemberName] string? propertyName = null) - { - if (comparer.Equals(oldValue, newValue)) - { - return false; - } - - OnPropertyChanging(propertyName); - - callback(newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// raises the event, updates the property and then raises the - /// event. The behavior mirrors that of , - /// with the difference being that this method is used to relay properties from a wrapped model in the - /// current instance. This type is useful when creating wrapping, bindable objects that operate over - /// models that lack support for notification (eg. for CRUD operations). - /// Suppose we have this model (eg. for a database row in a table): - /// - /// public class Person - /// { - /// public string Name { get; set; } - /// } - /// - /// We can then use a property to wrap instances of this type into our observable model (which supports - /// notifications), injecting the notification to the properties of that model, like so: - /// - /// public class BindablePerson : ObservableObject - /// { - /// public Model { get; } - /// - /// public BindablePerson(Person model) - /// { - /// Model = model; - /// } - /// - /// public string Name - /// { - /// get => Model.Name; - /// set => Set(Model.Name, value, Model, (model, name) => model.Name = name); - /// } - /// } - /// - /// This way we can then use the wrapping object in our application, and all those "proxy" properties will - /// also raise notifications when changed. Note that this method is not meant to be a replacement for - /// , and it should only be used when relaying properties to a model that - /// doesn't support notifications, and only if you can't implement notifications to that model directly (eg. by having - /// it inherit from ). The syntax relies on passing the target model and a stateless callback - /// to allow the C# compiler to cache the function, which results in much better performance and no memory usage. - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The model containing the property being updated. - /// The callback to invoke to set the target property value, if a change has occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The and events are not - /// raised if the current and new value for the target property are the same. - /// - protected bool SetProperty(T oldValue, T newValue, TModel model, Action callback, [CallerMemberName] string? propertyName = null) - where TModel : class - { - if (EqualityComparer.Default.Equals(oldValue, newValue)) - { - return false; - } - - OnPropertyChanging(propertyName); - - callback(model, newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// raises the event, updates the property and then raises the - /// event. The behavior mirrors that of , - /// with the difference being that this method is used to relay properties from a wrapped model in the - /// current instance. See additional notes about this overload in . - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// The model containing the property being updated. - /// The callback to invoke to set the target property value, if a change has occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, TModel model, Action callback, [CallerMemberName] string? propertyName = null) - where TModel : class - { - if (comparer.Equals(oldValue, newValue)) - { - return false; - } - - OnPropertyChanging(propertyName); - - callback(model, newValue); - - OnPropertyChanged(propertyName); - - return true; - } - - /// - /// Compares the current and new values for a given field (which should be the backing - /// field for a property). If the value has changed, raises the - /// event, updates the field and then raises the event. - /// The behavior mirrors that of , with the difference being that - /// this method will also monitor the new value of the property (a generic ) and will also - /// raise the again for the target property when it completes. - /// This can be used to update bindings observing that or any of its properties. - /// This method and its overload specifically rely on the type, which needs - /// to be used in the backing field for the target property. The field doesn't need to be - /// initialized, as this method will take care of doing that automatically. The - /// type also includes an implicit operator, so it can be assigned to any instance directly. - /// Here is a sample property declaration using this method: - /// - /// private TaskNotifier myTask; - /// - /// public Task MyTask - /// { - /// get => myTask; - /// private set => SetAndNotifyOnCompletion(ref myTask, value); - /// } - /// - /// - /// The field notifier to modify. - /// The property's value after the change occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The and events are not raised if the current - /// and new value for the target property are the same. The return value being only - /// indicates that the new value being assigned to is different than the previous one, - /// and it does not mean the new instance passed as argument is in any particular state. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null) - { - // We invoke the overload with a callback here to avoid code duplication, and simply pass an empty callback. - // The lambda expression here is transformed by the C# compiler into an empty closure class with a - // static singleton field containing a closure instance, and another caching the instantiated Action - // instance. This will result in no further allocations after the first time this method is called for a given - // generic type. We only pay the cost of the virtual call to the delegate, but this is not performance critical - // code and that overhead would still be much lower than the rest of the method anyway, so that's fine. - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName); - } - - /// - /// Compares the current and new values for a given field (which should be the backing - /// field for a property). If the value has changed, raises the - /// event, updates the field and then raises the event. - /// This method is just like , - /// with the difference being an extra parameter with a callback being invoked - /// either immediately, if the new task has already completed or is , or upon completion. - /// - /// The field notifier to modify. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The and events are not raised - /// if the current and new value for the target property are the same. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action callback, [CallerMemberName] string? propertyName = null) - { - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName); - } - - /// - /// Compares the current and new values for a given field (which should be the backing - /// field for a property). If the value has changed, raises the - /// event, updates the field and then raises the event. - /// The behavior mirrors that of , with the difference being that - /// this method will also monitor the new value of the property (a generic ) and will also - /// raise the again for the target property when it completes. - /// This can be used to update bindings observing that or any of its properties. - /// This method and its overload specifically rely on the type, which needs - /// to be used in the backing field for the target property. The field doesn't need to be - /// initialized, as this method will take care of doing that automatically. The - /// type also includes an implicit operator, so it can be assigned to any instance directly. - /// Here is a sample property declaration using this method: - /// - /// private TaskNotifier<int> myTask; - /// - /// public Task<int> MyTask - /// { - /// get => myTask; - /// private set => SetAndNotifyOnCompletion(ref myTask, value); - /// } - /// - /// - /// The type of result for the to set and monitor. - /// The field notifier to modify. - /// The property's value after the change occurred. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The and events are not raised if the current - /// and new value for the target property are the same. The return value being only - /// indicates that the new value being assigned to is different than the previous one, - /// and it does not mean the new instance passed as argument is in any particular state. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, [CallerMemberName] string? propertyName = null) - { - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, static _ => { }, propertyName); - } - - /// - /// Compares the current and new values for a given field (which should be the backing - /// field for a property). If the value has changed, raises the - /// event, updates the field and then raises the event. - /// This method is just like , - /// with the difference being an extra parameter with a callback being invoked - /// either immediately, if the new task has already completed or is , or upon completion. - /// - /// The type of result for the to set and monitor. - /// The field notifier to modify. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// The and events are not raised - /// if the current and new value for the target property are the same. - /// - protected bool SetPropertyAndNotifyOnCompletion([NotNull] ref TaskNotifier? taskNotifier, Task? newValue, Action?> callback, [CallerMemberName] string? propertyName = null) - { - return SetPropertyAndNotifyOnCompletion(taskNotifier ??= new(), newValue, callback, propertyName); - } - - /// - /// Implements the notification logic for the related methods. - /// - /// The type of to set and monitor. - /// The field notifier. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - private bool SetPropertyAndNotifyOnCompletion(ITaskNotifier taskNotifier, TTask? newValue, Action callback, [CallerMemberName] string? propertyName = null) - where TTask : Task - { - if (ReferenceEquals(taskNotifier.Task, newValue)) - { - return false; - } - - // Check the status of the new task before assigning it to the - // target field. This is so that in case the task is either - // null or already completed, we can avoid the overhead of - // scheduling the method to monitor its completion. - bool isAlreadyCompletedOrNull = newValue?.IsCompleted ?? true; - - OnPropertyChanging(propertyName); - - taskNotifier.Task = newValue; - - OnPropertyChanged(propertyName); - - // If the input task is either null or already completed, we don't need to - // execute the additional logic to monitor its completion, so we can just bypass - // the rest of the method and return that the field changed here. The return value - // does not indicate that the task itself has completed, but just that the property - // value itself has changed (ie. the referenced task instance has changed). - // This mirrors the return value of all the other synchronous Set methods as well. - if (isAlreadyCompletedOrNull) - { - callback(newValue); - - return true; - } - - // We use a local async function here so that the main method can - // remain synchronous and return a value that can be immediately - // used by the caller. This mirrors Set(ref T, T, string). - // We use an async void function instead of a Task-returning function - // so that if a binding update caused by the property change notification - // causes a crash, it is immediately reported in the application instead of - // the exception being ignored (as the returned task wouldn't be awaited), - // which would result in a confusing behavior for users. - async void MonitorTask() - { - try - { - // Await the task and ignore any exceptions - await newValue!; - } - catch - { - } - - // Only notify if the property hasn't changed - if (ReferenceEquals(taskNotifier.Task, newValue)) - { - OnPropertyChanged(propertyName); - } - - callback(newValue); - } - - MonitorTask(); - - return true; - } - - /// - /// An interface for task notifiers of a specified type. - /// - /// The type of value to store. - private interface ITaskNotifier - where TTask : Task - { - /// - /// Gets or sets the wrapped value. - /// - TTask? Task { get; set; } - } - - /// - /// A wrapping class that can hold a value. - /// - protected sealed class TaskNotifier : ITaskNotifier - { - /// - /// Initializes a new instance of the class. - /// - internal TaskNotifier() - { - } - - private Task? task; - - /// - Task? ITaskNotifier.Task - { - get => this.task; - set => this.task = value; - } - - /// - /// Unwraps the value stored in the current instance. - /// - /// The input instance. - public static implicit operator Task?(TaskNotifier? notifier) - { - return notifier?.task; - } - } - - /// - /// A wrapping class that can hold a value. - /// - /// The type of value for the wrapped instance. - protected sealed class TaskNotifier : ITaskNotifier> - { - /// - /// Initializes a new instance of the class. - /// - internal TaskNotifier() - { - } - - private Task? task; - - /// - Task? ITaskNotifier>.Task - { - get => this.task; - set => this.task = value; - } - - /// - /// Unwraps the value stored in the current instance. - /// - /// The input instance. - public static implicit operator Task?(TaskNotifier? notifier) - { - return notifier?.task; - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableRecipient.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableRecipient.cs deleted file mode 100644 index 23e0d0bb6bb..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableRecipient.cs +++ /dev/null @@ -1,305 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1512 - -// This file is inspired from the MvvmLight library (lbugnion/MvvmLight), -// more info in ThirdPartyNotices.txt in the root of the project. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.Mvvm.Messaging; -using Microsoft.Toolkit.Mvvm.Messaging.Messages; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// A base class for observable objects that also acts as recipients for messages. This class is an extension of - /// which also provides built-in support to use the type. - /// - public abstract class ObservableRecipient : ObservableObject - { - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor will produce an instance that will use the instance - /// to perform requested operations. It will also be available locally through the property. - /// - protected ObservableRecipient() - : this(WeakReferenceMessenger.Default) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The instance to use to send messages. - protected ObservableRecipient(IMessenger messenger) - { - Messenger = messenger; - } - - /// - /// Gets the instance in use. - /// - protected IMessenger Messenger { get; } - - private bool isActive; - - /// - /// Gets or sets a value indicating whether the current view model is currently active. - /// - public bool IsActive - { - get => this.isActive; - set - { - if (SetProperty(ref this.isActive, value, true)) - { - if (value) - { - OnActivated(); - } - else - { - OnDeactivated(); - } - } - } - } - - /// - /// Raised whenever the property is set to . - /// Use this method to register to messages and do other initialization for this instance. - /// - /// - /// The base implementation registers all messages for this recipients that have been declared - /// explicitly through the interface, using the default channel. - /// For more details on how this works, see the method. - /// If you need more fine tuned control, want to register messages individually or just prefer - /// the lambda-style syntax for message registration, override this method and register manually. - /// - protected virtual void OnActivated() - { - Messenger.RegisterAll(this); - } - - /// - /// Raised whenever the property is set to . - /// Use this method to unregister from messages and do general cleanup for this instance. - /// - /// - /// The base implementation unregisters all messages for this recipient. It does so by - /// invoking , which removes all registered - /// handlers for a given subscriber, regardless of what token was used to register them. - /// That is, all registered handlers across all subscription channels will be removed. - /// - protected virtual void OnDeactivated() - { - Messenger.UnregisterAll(this); - } - - /// - /// Broadcasts a with the specified - /// parameters, without using any particular token (so using the default channel). - /// - /// The type of the property that changed. - /// The value of the property before it changed. - /// The value of the property after it changed. - /// The name of the property that changed. - /// - /// You should override this method if you wish to customize the channel being - /// used to send the message (eg. if you need to use a specific token for the channel). - /// - protected virtual void Broadcast(T oldValue, T newValue, string? propertyName) - { - PropertyChangedMessage message = new(this, propertyName, oldValue, newValue); - - _ = Messenger.Send(message); - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// If , will also be invoked. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// This method is just like , just with the addition - /// of the parameter. As such, following the behavior of the base method, - /// the and events - /// are not raised if the current and new value for the target property are the same. - /// - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, bool broadcast, [CallerMemberName] string? propertyName = null) - { - T oldValue = field; - - // We duplicate the code as in the base class here to leverage - // the intrinsics support for EqualityComparer.Default.Equals. - bool propertyChanged = SetProperty(ref field, newValue, propertyName); - - if (propertyChanged && broadcast) - { - Broadcast(oldValue, newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// If , will also be invoked. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer comparer, bool broadcast, [CallerMemberName] string? propertyName = null) - { - T oldValue = field; - - bool propertyChanged = SetProperty(ref field, newValue, comparer, propertyName); - - if (propertyChanged && broadcast) - { - Broadcast(oldValue, newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. Similarly to - /// the method, this overload should only be - /// used when can't be used directly. - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// If , will also be invoked. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// This method is just like , just with the addition - /// of the parameter. As such, following the behavior of the base method, - /// the and events - /// are not raised if the current and new value for the target property are the same. - /// - protected bool SetProperty(T oldValue, T newValue, Action callback, bool broadcast, [CallerMemberName] string? propertyName = null) - { - bool propertyChanged = SetProperty(oldValue, newValue, callback, propertyName); - - if (propertyChanged && broadcast) - { - Broadcast(oldValue, newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// A callback to invoke to update the property value. - /// If , will also be invoked. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, Action callback, bool broadcast, [CallerMemberName] string? propertyName = null) - { - bool propertyChanged = SetProperty(oldValue, newValue, comparer, callback, propertyName); - - if (propertyChanged && broadcast) - { - Broadcast(oldValue, newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// raises the event, updates the property and then raises the - /// event. The behavior mirrors that of - /// , with the difference being that this - /// method is used to relay properties from a wrapped model in the current instance. For more info, see the docs for - /// . - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The model - /// The callback to invoke to set the target property value, if a change has occurred. - /// If , will also be invoked. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, TModel model, Action callback, bool broadcast, [CallerMemberName] string? propertyName = null) - where TModel : class - { - bool propertyChanged = SetProperty(oldValue, newValue, model, callback, propertyName); - - if (propertyChanged && broadcast) - { - Broadcast(oldValue, newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// raises the event, updates the property and then raises the - /// event. The behavior mirrors that of - /// , - /// with the difference being that this method is used to relay properties from a wrapped model in the - /// current instance. For more info, see the docs for - /// . - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// The model - /// The callback to invoke to set the target property value, if a change has occurred. - /// If , will also be invoked. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, TModel model, Action callback, bool broadcast, [CallerMemberName] string? propertyName = null) - where TModel : class - { - bool propertyChanged = SetProperty(oldValue, newValue, comparer, model, callback, propertyName); - - if (propertyChanged && broadcast) - { - Broadcast(oldValue, newValue, propertyName); - } - - return propertyChanged; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableValidator.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableValidator.cs deleted file mode 100644 index 9ed3481817b..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/ObservableValidator.cs +++ /dev/null @@ -1,772 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel -{ - /// - /// A base class for objects implementing the interface. This class - /// also inherits from , so it can be used for observable items too. - /// - public abstract class ObservableValidator : ObservableObject, INotifyDataErrorInfo - { - /// - /// The instance used to track compiled delegates to validate entities. - /// - private static readonly ConditionalWeakTable> EntityValidatorMap = new(); - - /// - /// The instance used to track display names for properties to validate. - /// - /// - /// This is necessary because we want to reuse the same instance for all validations, but - /// with the same behavior with repsect to formatted names that new instances would have provided. The issue is that the - /// property is not refreshed when we set , - /// so we need to replicate the same logic to retrieve the right display name for properties to validate and update that - /// property manually right before passing the context to and proceed with the normal functionality. - /// - private static readonly ConditionalWeakTable> DisplayNamesMap = new(); - - /// - /// The cached for . - /// - private static readonly PropertyChangedEventArgs HasErrorsChangedEventArgs = new(nameof(HasErrors)); - - /// - /// The instance currenty in use. - /// - private readonly ValidationContext validationContext; - - /// - /// The instance used to store previous validation results. - /// - private readonly Dictionary> errors = new(); - - /// - /// Indicates the total number of properties with errors (not total errors). - /// This is used to allow to operate in O(1) time, as it can just - /// check whether this value is not 0 instead of having to traverse . - /// - private int totalErrors; - - /// - public event EventHandler? ErrorsChanged; - - /// - /// Initializes a new instance of the class. - /// This constructor will create a new that will - /// be used to validate all properties, which will reference the current instance - /// and no additional services or validation properties and settings. - /// - protected ObservableValidator() - { - this.validationContext = new ValidationContext(this); - } - - /// - /// Initializes a new instance of the class. - /// This constructor will create a new that will - /// be used to validate all properties, which will reference the current instance. - /// - /// A set of key/value pairs to make available to consumers. - protected ObservableValidator(IDictionary? items) - { - this.validationContext = new ValidationContext(this, items); - } - - /// - /// Initializes a new instance of the class. - /// This constructor will create a new that will - /// be used to validate all properties, which will reference the current instance. - /// - /// An instance to make available during validation. - /// A set of key/value pairs to make available to consumers. - protected ObservableValidator(IServiceProvider? serviceProvider, IDictionary? items) - { - this.validationContext = new ValidationContext(this, serviceProvider, items); - } - - /// - /// Initializes a new instance of the class. - /// This constructor will store the input instance, - /// and it will use it to validate all properties for the current viewmodel. - /// - /// - /// The instance to use to validate properties. - /// - /// This instance will be passed to all - /// calls executed by the current viewmodel, and its property will be updated every time - /// before the call is made to set the name of the property being validated. The property name will not be reset after that, so the - /// value of will always indicate the name of the last property that was validated, if any. - /// - /// - protected ObservableValidator(ValidationContext validationContext) - { - this.validationContext = validationContext; - } - - /// - public bool HasErrors => this.totalErrors > 0; - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// If , will also be validated. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// This method is just like , just with the addition - /// of the parameter. If that is set to , the new value will be - /// validated and will be raised if needed. Following the behavior of the base method, - /// the and events - /// are not raised if the current and new value for the target property are the same. - /// - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, bool validate, [CallerMemberName] string? propertyName = null) - { - bool propertyChanged = SetProperty(ref field, newValue, propertyName); - - if (propertyChanged && validate) - { - ValidateProperty(newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// If , will also be validated. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty([NotNullIfNotNull("newValue")] ref T field, T newValue, IEqualityComparer comparer, bool validate, [CallerMemberName] string? propertyName = null) - { - bool propertyChanged = SetProperty(ref field, newValue, comparer, propertyName); - - if (propertyChanged && validate) - { - ValidateProperty(newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. Similarly to - /// the method, this overload should only be - /// used when can't be used directly. - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// If , will also be validated. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - /// - /// This method is just like , just with the addition - /// of the parameter. As such, following the behavior of the base method, - /// the and events - /// are not raised if the current and new value for the target property are the same. - /// - protected bool SetProperty(T oldValue, T newValue, Action callback, bool validate, [CallerMemberName] string? propertyName = null) - { - bool propertyChanged = SetProperty(oldValue, newValue, callback, propertyName); - - if (propertyChanged && validate) - { - ValidateProperty(newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given property. If the value has changed, - /// raises the event, updates the property with - /// the new value, then raises the event. - /// See additional notes about this overload in . - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// A callback to invoke to update the property value. - /// If , will also be validated. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, Action callback, bool validate, [CallerMemberName] string? propertyName = null) - { - bool propertyChanged = SetProperty(oldValue, newValue, comparer, callback, propertyName); - - if (propertyChanged && validate) - { - ValidateProperty(newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// raises the event, updates the property and then raises the - /// event. The behavior mirrors that of - /// , with the difference being that this - /// method is used to relay properties from a wrapped model in the current instance. For more info, see the docs for - /// . - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The model - /// The callback to invoke to set the target property value, if a change has occurred. - /// If , will also be validated. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, TModel model, Action callback, bool validate, [CallerMemberName] string? propertyName = null) - where TModel : class - { - bool propertyChanged = SetProperty(oldValue, newValue, model, callback, propertyName); - - if (propertyChanged && validate) - { - ValidateProperty(newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Compares the current and new values for a given nested property. If the value has changed, - /// raises the event, updates the property and then raises the - /// event. The behavior mirrors that of - /// , - /// with the difference being that this method is used to relay properties from a wrapped model in the - /// current instance. For more info, see the docs for - /// . - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// The model - /// The callback to invoke to set the target property value, if a change has occurred. - /// If , will also be validated. - /// (optional) The name of the property that changed. - /// if the property was changed, otherwise. - protected bool SetProperty(T oldValue, T newValue, IEqualityComparer comparer, TModel model, Action callback, bool validate, [CallerMemberName] string? propertyName = null) - where TModel : class - { - bool propertyChanged = SetProperty(oldValue, newValue, comparer, model, callback, propertyName); - - if (propertyChanged && validate) - { - ValidateProperty(newValue, propertyName); - } - - return propertyChanged; - } - - /// - /// Tries to validate a new value for a specified property. If the validation is successful, - /// is called, otherwise no state change is performed. - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// The resulting validation errors, if any. - /// (optional) The name of the property that changed. - /// Whether the validation was successful and the property value changed as well. - protected bool TrySetProperty(ref T field, T newValue, out IReadOnlyCollection errors, [CallerMemberName] string? propertyName = null) - { - return TryValidateProperty(newValue, propertyName, out errors) && - SetProperty(ref field, newValue, propertyName); - } - - /// - /// Tries to validate a new value for a specified property. If the validation is successful, - /// is called, otherwise no state change is performed. - /// - /// The type of the property that changed. - /// The field storing the property's value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// The resulting validation errors, if any. - /// (optional) The name of the property that changed. - /// Whether the validation was successful and the property value changed as well. - protected bool TrySetProperty(ref T field, T newValue, IEqualityComparer comparer, out IReadOnlyCollection errors, [CallerMemberName] string? propertyName = null) - { - return TryValidateProperty(newValue, propertyName, out errors) && - SetProperty(ref field, newValue, comparer, propertyName); - } - - /// - /// Tries to validate a new value for a specified property. If the validation is successful, - /// is called, otherwise no state change is performed. - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// A callback to invoke to update the property value. - /// The resulting validation errors, if any. - /// (optional) The name of the property that changed. - /// Whether the validation was successful and the property value changed as well. - protected bool TrySetProperty(T oldValue, T newValue, Action callback, out IReadOnlyCollection errors, [CallerMemberName] string? propertyName = null) - { - return TryValidateProperty(newValue, propertyName, out errors) && - SetProperty(oldValue, newValue, callback, propertyName); - } - - /// - /// Tries to validate a new value for a specified property. If the validation is successful, - /// is called, otherwise no state change is performed. - /// - /// The type of the property that changed. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// A callback to invoke to update the property value. - /// The resulting validation errors, if any. - /// (optional) The name of the property that changed. - /// Whether the validation was successful and the property value changed as well. - protected bool TrySetProperty(T oldValue, T newValue, IEqualityComparer comparer, Action callback, out IReadOnlyCollection errors, [CallerMemberName] string? propertyName = null) - { - return TryValidateProperty(newValue, propertyName, out errors) && - SetProperty(oldValue, newValue, comparer, callback, propertyName); - } - - /// - /// Tries to validate a new value for a specified property. If the validation is successful, - /// is called, otherwise no state change is performed. - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The model - /// The callback to invoke to set the target property value, if a change has occurred. - /// The resulting validation errors, if any. - /// (optional) The name of the property that changed. - /// Whether the validation was successful and the property value changed as well. - protected bool TrySetProperty(T oldValue, T newValue, TModel model, Action callback, out IReadOnlyCollection errors, [CallerMemberName] string? propertyName = null) - where TModel : class - { - return TryValidateProperty(newValue, propertyName, out errors) && - SetProperty(oldValue, newValue, model, callback, propertyName); - } - - /// - /// Tries to validate a new value for a specified property. If the validation is successful, - /// is called, otherwise no state change is performed. - /// - /// The type of model whose property (or field) to set. - /// The type of property (or field) to set. - /// The current property value. - /// The property's value after the change occurred. - /// The instance to use to compare the input values. - /// The model - /// The callback to invoke to set the target property value, if a change has occurred. - /// The resulting validation errors, if any. - /// (optional) The name of the property that changed. - /// Whether the validation was successful and the property value changed as well. - protected bool TrySetProperty(T oldValue, T newValue, IEqualityComparer comparer, TModel model, Action callback, out IReadOnlyCollection errors, [CallerMemberName] string? propertyName = null) - where TModel : class - { - return TryValidateProperty(newValue, propertyName, out errors) && - SetProperty(oldValue, newValue, comparer, model, callback, propertyName); - } - - /// - /// Clears the validation errors for a specified property or for the entire entity. - /// - /// - /// The name of the property to clear validation errors for. - /// If a or empty name is used, all entity-level errors will be cleared. - /// - protected void ClearErrors(string? propertyName = null) - { - // Clear entity-level errors when the target property is null or empty - if (string.IsNullOrEmpty(propertyName)) - { - ClearAllErrors(); - } - else - { - ClearErrorsForProperty(propertyName!); - } - } - - /// - [Pure] - public IEnumerable GetErrors(string? propertyName = null) - { - // Get entity-level errors when the target property is null or empty - if (string.IsNullOrEmpty(propertyName)) - { - // Local function to gather all the entity-level errors - [Pure] - [MethodImpl(MethodImplOptions.NoInlining)] - IEnumerable GetAllErrors() - { - return this.errors.Values.SelectMany(static errors => errors); - } - - return GetAllErrors(); - } - - // Property-level errors, if any - if (this.errors.TryGetValue(propertyName!, out List? errors)) - { - return errors; - } - - // The INotifyDataErrorInfo.GetErrors method doesn't specify exactly what to - // return when the input property name is invalid, but given that the return - // type is marked as a non-nullable reference type, here we're returning an - // empty array to respect the contract. This also matches the behavior of - // this method whenever errors for a valid properties are retrieved. - return Array.Empty(); - } - - /// - [Pure] - IEnumerable INotifyDataErrorInfo.GetErrors(string? propertyName) => GetErrors(propertyName); - - /// - /// Validates all the properties in the current instance and updates all the tracked errors. - /// If any changes are detected, the event will be raised. - /// - /// - /// Only public instance properties (excluding custom indexers) that have at least one - /// applied to them will be validated. All other - /// members in the current instance will be ignored. None of the processed properties - /// will be modified - they will only be used to retrieve their values and validate them. - /// - protected void ValidateAllProperties() - { - // Fast path that tries to create a delegate from a generated type-specific method. This - // is used to make this method more AOT-friendly and faster, as there is no dynamic code. - static Action GetValidationAction(Type type) - { - if (type.Assembly.GetType("Microsoft.Toolkit.Mvvm.ComponentModel.__Internals.__ObservableValidatorExtensions") is Type extensionsType && - extensionsType.GetMethod("CreateAllPropertiesValidator", new[] { type }) is MethodInfo methodInfo) - { - return (Action)methodInfo.Invoke(null, new object?[] { null })!; - } - - return GetValidationActionFallback(type); - } - - // Fallback method to create the delegate with a compiled LINQ expression - static Action GetValidationActionFallback(Type type) - { - // Get the collection of all properties to validate - (string Name, MethodInfo GetMethod)[] validatableProperties = ( - from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public) - where property.GetIndexParameters().Length == 0 && - property.GetCustomAttributes(true).Any() - let getMethod = property.GetMethod - where getMethod is not null - select (property.Name, getMethod)).ToArray(); - - // Short path if there are no properties to validate - if (validatableProperties.Length == 0) - { - return static _ => { }; - } - - // MyViewModel inst0 = (MyViewModel)arg0; - ParameterExpression arg0 = Expression.Parameter(typeof(object)); - UnaryExpression inst0 = Expression.Convert(arg0, type); - - // Get a reference to ValidateProperty(object, string) - MethodInfo validateMethod = typeof(ObservableValidator).GetMethod(nameof(ValidateProperty), BindingFlags.Instance | BindingFlags.NonPublic)!; - - // We want a single compiled LINQ expression that validates all properties in the - // actual type of the executing viewmodel at once. We do this by creating a block - // expression with the unrolled invocations of all properties to validate. - // Essentially, the body will contain the following code: - // =============================================================================== - // { - // inst0.ValidateProperty(inst0.Property0, nameof(MyViewModel.Property0)); - // inst0.ValidateProperty(inst0.Property1, nameof(MyViewModel.Property1)); - // ... - // inst0.ValidateProperty(inst0.PropertyN, nameof(MyViewModel.PropertyN)); - // } - // =============================================================================== - // We also add an explicit object conversion to represent boxing, if a given property - // is a value type. It will just be a no-op if the value is a reference type already. - // Note that this generated code is technically accessing a protected method from - // ObservableValidator externally, but that is fine because IL doesn't really have - // a concept of member visibility, that's purely a C# build-time feature. - BlockExpression body = Expression.Block( - from property in validatableProperties - select Expression.Call(inst0, validateMethod, new Expression[] - { - Expression.Convert(Expression.Call(inst0, property.GetMethod), typeof(object)), - Expression.Constant(property.Name) - })); - - return Expression.Lambda>(body, arg0).Compile(); - } - - // Get or compute the cached list of properties to validate. Here we're using a static lambda to ensure the - // delegate is cached by the C# compiler, see the related issue at https://github.com/dotnet/roslyn/issues/5835. - EntityValidatorMap.GetValue(GetType(), static t => GetValidationAction(t))(this); - } - - /// - /// Validates a property with a specified name and a given input value. - /// If any changes are detected, the event will be raised. - /// - /// The value to test for the specified property. - /// The name of the property to validate. - /// Thrown when is . - protected internal void ValidateProperty(object? value, [CallerMemberName] string? propertyName = null) - { - if (propertyName is null) - { - ThrowArgumentNullExceptionForNullPropertyName(); - } - - // Check if the property had already been previously validated, and if so retrieve - // the reusable list of validation errors from the errors dictionary. This list is - // used to add new validation errors below, if any are produced by the validator. - // If the property isn't present in the dictionary, add it now to avoid allocations. - if (!this.errors.TryGetValue(propertyName!, out List? propertyErrors)) - { - propertyErrors = new(); - - this.errors.Add(propertyName!, propertyErrors); - } - - bool errorsChanged = false; - - // Clear the errors for the specified property, if any - if (propertyErrors.Count > 0) - { - propertyErrors.Clear(); - - errorsChanged = true; - } - - // Validate the property, by adding new errors to the existing list - this.validationContext.MemberName = propertyName; - this.validationContext.DisplayName = GetDisplayNameForProperty(propertyName!); - - bool isValid = Validator.TryValidateProperty(value, this.validationContext, propertyErrors); - - // Update the shared counter for the number of errors, and raise the - // property changed event if necessary. We decrement the number of total - // errors if the current property is valid but it wasn't so before this - // validation, and we increment it if the validation failed after being - // correct before. The property changed event is raised whenever the - // number of total errors is either decremented to 0, or incremented to 1. - if (isValid) - { - if (errorsChanged) - { - this.totalErrors--; - - if (this.totalErrors == 0) - { - OnPropertyChanged(HasErrorsChangedEventArgs); - } - } - } - else if (!errorsChanged) - { - this.totalErrors++; - - if (this.totalErrors == 1) - { - OnPropertyChanged(HasErrorsChangedEventArgs); - } - } - - // Only raise the event once if needed. This happens either when the target property - // had existing errors and is now valid, or if the validation has failed and there are - // new errors to broadcast, regardless of the previous validation state for the property. - if (errorsChanged || !isValid) - { - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); - } - } - - /// - /// Tries to validate a property with a specified name and a given input value, and returns - /// the computed errors, if any. If the property is valid, it is assumed that its value is - /// about to be set in the current object. Otherwise, no observable local state is modified. - /// - /// The value to test for the specified property. - /// The name of the property to validate. - /// The resulting validation errors, if any. - /// Thrown when is . - private bool TryValidateProperty(object? value, string? propertyName, out IReadOnlyCollection errors) - { - if (propertyName is null) - { - ThrowArgumentNullExceptionForNullPropertyName(); - } - - // Add the cached errors list for later use. - if (!this.errors.TryGetValue(propertyName!, out List? propertyErrors)) - { - propertyErrors = new(); - - this.errors.Add(propertyName!, propertyErrors); - } - - bool hasErrors = propertyErrors.Count > 0; - - List localErrors = new(); - - // Validate the property, by adding new errors to the local list - this.validationContext.MemberName = propertyName; - this.validationContext.DisplayName = GetDisplayNameForProperty(propertyName!); - - bool isValid = Validator.TryValidateProperty(value, this.validationContext, localErrors); - - // We only modify the state if the property is valid and it wasn't so before. In this case, we - // clear the cached list of errors (which is visible to consumers) and raise the necessary events. - if (isValid && hasErrors) - { - propertyErrors.Clear(); - - this.totalErrors--; - - if (this.totalErrors == 0) - { - OnPropertyChanged(HasErrorsChangedEventArgs); - } - - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); - } - - errors = localErrors; - - return isValid; - } - - /// - /// Clears all the current errors for the entire entity. - /// - private void ClearAllErrors() - { - if (this.totalErrors == 0) - { - return; - } - - // Clear the errors for all properties with at least one error, and raise the - // ErrorsChanged event for those properties. Other properties will be ignored. - foreach (var propertyInfo in this.errors) - { - bool hasErrors = propertyInfo.Value.Count > 0; - - propertyInfo.Value.Clear(); - - if (hasErrors) - { - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyInfo.Key)); - } - } - - this.totalErrors = 0; - - OnPropertyChanged(HasErrorsChangedEventArgs); - } - - /// - /// Clears all the current errors for a target property. - /// - /// The name of the property to clear errors for. - private void ClearErrorsForProperty(string propertyName) - { - if (!this.errors.TryGetValue(propertyName!, out List? propertyErrors) || - propertyErrors.Count == 0) - { - return; - } - - propertyErrors.Clear(); - - this.totalErrors--; - - if (this.totalErrors == 0) - { - OnPropertyChanged(HasErrorsChangedEventArgs); - } - - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); - } - - /// - /// Gets the display name for a given property. It could be a custom name or just the property name. - /// - /// The target property name being validated. - /// The display name for the property. - private string GetDisplayNameForProperty(string propertyName) - { - static Dictionary GetDisplayNames(Type type) - { - Dictionary displayNames = new(); - - foreach (PropertyInfo property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) - { - if (property.GetCustomAttribute() is DisplayAttribute attribute && - attribute.GetName() is string displayName) - { - displayNames.Add(property.Name, displayName); - } - } - - return displayNames; - } - - // This method replicates the logic of DisplayName and GetDisplayName from the - // ValidationContext class. See the original source in the BCL for more details. - _ = DisplayNamesMap.GetValue(GetType(), static t => GetDisplayNames(t)).TryGetValue(propertyName, out string? displayName); - - return displayName ?? propertyName; - } - -#pragma warning disable SA1204 - /// - /// Throws an when a property name given as input is . - /// - private static void ThrowArgumentNullExceptionForNullPropertyName() - { - throw new ArgumentNullException("propertyName", "The input property name cannot be null when validating a property"); - } -#pragma warning restore SA1204 - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/ComponentModel/__Internals/__ObservableValidatorHelper.cs b/Microsoft.Toolkit.Mvvm/ComponentModel/__Internals/__ObservableValidatorHelper.cs deleted file mode 100644 index bd9ee7ede80..00000000000 --- a/Microsoft.Toolkit.Mvvm/ComponentModel/__Internals/__ObservableValidatorHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1300 - -using System; -using System.ComponentModel; - -namespace Microsoft.Toolkit.Mvvm.ComponentModel.__Internals -{ - /// - /// An internal helper to support the source generator APIs related to . - /// This type is not intended to be used directly by user code. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("This type is not intended to be used directly by user code")] - public static class __ObservableValidatorHelper - { - /// - /// Invokes externally on a target instance. - /// - /// The target instance. - /// The value to test for the specified property. - /// The name of the property to validate. - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("This method is not intended to be called directly by user code")] - public static void ValidateProperty(ObservableValidator instance, object? value, string propertyName) - { - instance.ValidateProperty(value, propertyName); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/DependencyInjection/Ioc.cs b/Microsoft.Toolkit.Mvvm/DependencyInjection/Ioc.cs deleted file mode 100644 index 1a259221e18..00000000000 --- a/Microsoft.Toolkit.Mvvm/DependencyInjection/Ioc.cs +++ /dev/null @@ -1,167 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading; - -#nullable enable - -namespace Microsoft.Toolkit.Mvvm.DependencyInjection -{ - /// - /// A type that facilitates the use of the type. - /// The provides the ability to configure services in a singleton, thread-safe - /// service provider instance, which can then be used to resolve service instances. - /// The first step to use this feature is to declare some services, for instance: - /// - /// public interface ILogger - /// { - /// void Log(string text); - /// } - /// - /// - /// public class ConsoleLogger : ILogger - /// { - /// void Log(string text) => Console.WriteLine(text); - /// } - /// - /// Then the services configuration should then be done at startup, by calling the - /// method and passing an instance with the services to use. That instance can - /// be from any library offering dependency injection functionality, such as Microsoft.Extensions.DependencyInjection. - /// For instance, using that library, can be used as follows in this example: - /// - /// Ioc.Default.ConfigureServices( - /// new ServiceCollection() - /// .AddSingleton<ILogger, Logger>() - /// .BuildServiceProvider()); - /// - /// Finally, you can use the instance (which implements ) - /// to retrieve the service instances from anywhere in your application, by doing as follows: - /// - /// Ioc.Default.GetService<ILogger>().Log("Hello world!"); - /// - /// - public sealed class Ioc : IServiceProvider - { - /// - /// Gets the default instance. - /// - public static Ioc Default { get; } = new(); - - /// - /// The instance to use, if initialized. - /// - private volatile IServiceProvider? serviceProvider; - - /// - public object? GetService(Type serviceType) - { - // As per section I.12.6.6 of the official CLI ECMA-335 spec: - // "[...] read and write access to properly aligned memory locations no larger than the native - // word size is atomic when all the write accesses to a location are the same size. Atomic writes - // shall alter no bits other than those written. Unless explicit layout control is used [...], - // data elements no larger than the natural word size [...] shall be properly aligned. - // Object references shall be treated as though they are stored in the native word size." - // The field being accessed here is of native int size (reference type), and is only ever accessed - // directly and atomically by a compare exchange instruction (see below), or here. We can therefore - // assume this read is thread safe with respect to accesses to this property or to invocations to one - // of the available configuration methods. So we can just read the field directly and make the necessary - // check with our local copy, without the need of paying the locking overhead from this get accessor. - IServiceProvider? provider = this.serviceProvider; - - if (provider is null) - { - ThrowInvalidOperationExceptionForMissingInitialization(); - } - - return provider!.GetService(serviceType); - } - - /// - /// Tries to resolve an instance of a specified service type. - /// - /// The type of service to resolve. - /// An instance of the specified service, or . - /// Throw if the current instance has not been initialized. - public T? GetService() - where T : class - { - IServiceProvider? provider = this.serviceProvider; - - if (provider is null) - { - ThrowInvalidOperationExceptionForMissingInitialization(); - } - - return (T?)provider!.GetService(typeof(T)); - } - - /// - /// Resolves an instance of a specified service type. - /// - /// The type of service to resolve. - /// An instance of the specified service, or . - /// - /// Throw if the current instance has not been initialized, or if the - /// requested service type was not registered in the service provider currently in use. - /// - public T GetRequiredService() - where T : class - { - IServiceProvider? provider = this.serviceProvider; - - if (provider is null) - { - ThrowInvalidOperationExceptionForMissingInitialization(); - } - - T? service = (T?)provider!.GetService(typeof(T)); - - if (service is null) - { - ThrowInvalidOperationExceptionForUnregisteredType(); - } - - return service!; - } - - /// - /// Initializes the shared instance. - /// - /// The input instance to use. - public void ConfigureServices(IServiceProvider serviceProvider) - { - IServiceProvider? oldServices = Interlocked.CompareExchange(ref this.serviceProvider, serviceProvider, null); - - if (oldServices is not null) - { - ThrowInvalidOperationExceptionForRepeatedConfiguration(); - } - } - - /// - /// Throws an when the property is used before initialization. - /// - private static void ThrowInvalidOperationExceptionForMissingInitialization() - { - throw new InvalidOperationException("The service provider has not been configured yet"); - } - - /// - /// Throws an when the property is missing a type registration. - /// - private static void ThrowInvalidOperationExceptionForUnregisteredType() - { - throw new InvalidOperationException("The requested service type was not registered"); - } - - /// - /// Throws an when a configuration is attempted more than once. - /// - private static void ThrowInvalidOperationExceptionForRepeatedConfiguration() - { - throw new InvalidOperationException("The default service provider has already been configured"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand.cs b/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand.cs deleted file mode 100644 index 71a123cfa27..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand.cs +++ /dev/null @@ -1,186 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Toolkit.Mvvm.ComponentModel; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// A command that mirrors the functionality of , with the addition of - /// accepting a returning a as the execute - /// action, and providing an property that notifies changes when - /// is invoked and when the returned completes. - /// - public sealed class AsyncRelayCommand : ObservableObject, IAsyncRelayCommand - { - /// - /// The cached for . - /// - internal static readonly PropertyChangedEventArgs CanBeCanceledChangedEventArgs = new(nameof(CanBeCanceled)); - - /// - /// The cached for . - /// - internal static readonly PropertyChangedEventArgs IsCancellationRequestedChangedEventArgs = new(nameof(IsCancellationRequested)); - - /// - /// The cached for . - /// - internal static readonly PropertyChangedEventArgs IsRunningChangedEventArgs = new(nameof(IsRunning)); - - /// - /// The to invoke when is used. - /// - private readonly Func? execute; - - /// - /// The cancelable to invoke when is used. - /// - /// Only one between this and is not . - private readonly Func? cancelableExecute; - - /// - /// The optional action to invoke when is used. - /// - private readonly Func? canExecute; - - /// - /// The instance to use to cancel . - /// - /// This is only used when is not . - private CancellationTokenSource? cancellationTokenSource; - - /// - public event EventHandler? CanExecuteChanged; - - /// - /// Initializes a new instance of the class that can always execute. - /// - /// The execution logic. - public AsyncRelayCommand(Func execute) - { - this.execute = execute; - } - - /// - /// Initializes a new instance of the class that can always execute. - /// - /// The cancelable execution logic. - public AsyncRelayCommand(Func cancelableExecute) - { - this.cancelableExecute = cancelableExecute; - } - - /// - /// Initializes a new instance of the class. - /// - /// The execution logic. - /// The execution status logic. - public AsyncRelayCommand(Func execute, Func canExecute) - { - this.execute = execute; - this.canExecute = canExecute; - } - - /// - /// Initializes a new instance of the class. - /// - /// The cancelable execution logic. - /// The execution status logic. - public AsyncRelayCommand(Func cancelableExecute, Func canExecute) - { - this.cancelableExecute = cancelableExecute; - this.canExecute = canExecute; - } - - private TaskNotifier? executionTask; - - /// - public Task? ExecutionTask - { - get => this.executionTask; - private set - { - if (SetPropertyAndNotifyOnCompletion(ref this.executionTask, value, _ => - { - // When the task completes - OnPropertyChanged(IsRunningChangedEventArgs); - OnPropertyChanged(CanBeCanceledChangedEventArgs); - })) - { - // When setting the task - OnPropertyChanged(IsRunningChangedEventArgs); - OnPropertyChanged(CanBeCanceledChangedEventArgs); - } - } - } - - /// - public bool CanBeCanceled => this.cancelableExecute is not null && IsRunning; - - /// - public bool IsCancellationRequested => this.cancellationTokenSource?.IsCancellationRequested == true; - - /// - public bool IsRunning => ExecutionTask?.IsCompleted == false; - - /// - public void NotifyCanExecuteChanged() - { - CanExecuteChanged?.Invoke(this, EventArgs.Empty); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanExecute(object? parameter) - { - return this.canExecute?.Invoke() != false; - } - - /// - public void Execute(object? parameter) - { - _ = ExecuteAsync(parameter); - } - - /// - public Task ExecuteAsync(object? parameter) - { - if (CanExecute(parameter)) - { - // Non cancelable command delegate - if (this.execute is not null) - { - return ExecutionTask = this.execute(); - } - - // Cancel the previous operation, if one is pending - this.cancellationTokenSource?.Cancel(); - - CancellationTokenSource cancellationTokenSource = this.cancellationTokenSource = new(); - - OnPropertyChanged(IsCancellationRequestedChangedEventArgs); - - // Invoke the cancelable command delegate with a new linked token - return ExecutionTask = this.cancelableExecute!(cancellationTokenSource.Token); - } - - return Task.CompletedTask; - } - - /// - public void Cancel() - { - this.cancellationTokenSource?.Cancel(); - - OnPropertyChanged(IsCancellationRequestedChangedEventArgs); - OnPropertyChanged(CanBeCanceledChangedEventArgs); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand{T}.cs b/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand{T}.cs deleted file mode 100644 index d24b4705d82..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/AsyncRelayCommand{T}.cs +++ /dev/null @@ -1,196 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Toolkit.Mvvm.ComponentModel; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// A generic command that provides a more specific version of . - /// - /// The type of parameter being passed as input to the callbacks. - public sealed class AsyncRelayCommand : ObservableObject, IAsyncRelayCommand - { - /// - /// The to invoke when is used. - /// - private readonly Func? execute; - - /// - /// The cancelable to invoke when is used. - /// - private readonly Func? cancelableExecute; - - /// - /// The optional action to invoke when is used. - /// - private readonly Predicate? canExecute; - - /// - /// The instance to use to cancel . - /// - private CancellationTokenSource? cancellationTokenSource; - - /// - public event EventHandler? CanExecuteChanged; - - /// - /// Initializes a new instance of the class that can always execute. - /// - /// The execution logic. - /// See notes in . - public AsyncRelayCommand(Func execute) - { - this.execute = execute; - } - - /// - /// Initializes a new instance of the class that can always execute. - /// - /// The cancelable execution logic. - /// See notes in . - public AsyncRelayCommand(Func cancelableExecute) - { - this.cancelableExecute = cancelableExecute; - } - - /// - /// Initializes a new instance of the class. - /// - /// The execution logic. - /// The execution status logic. - /// See notes in . - public AsyncRelayCommand(Func execute, Predicate canExecute) - { - this.execute = execute; - this.canExecute = canExecute; - } - - /// - /// Initializes a new instance of the class. - /// - /// The cancelable execution logic. - /// The execution status logic. - /// See notes in . - public AsyncRelayCommand(Func cancelableExecute, Predicate canExecute) - { - this.cancelableExecute = cancelableExecute; - this.canExecute = canExecute; - } - - private TaskNotifier? executionTask; - - /// - public Task? ExecutionTask - { - get => this.executionTask; - private set - { - if (SetPropertyAndNotifyOnCompletion(ref this.executionTask, value, _ => - { - // When the task completes - OnPropertyChanged(AsyncRelayCommand.IsRunningChangedEventArgs); - OnPropertyChanged(AsyncRelayCommand.CanBeCanceledChangedEventArgs); - })) - { - // When setting the task - OnPropertyChanged(AsyncRelayCommand.IsRunningChangedEventArgs); - OnPropertyChanged(AsyncRelayCommand.CanBeCanceledChangedEventArgs); - } - } - } - - /// - public bool CanBeCanceled => this.cancelableExecute is not null && IsRunning; - - /// - public bool IsCancellationRequested => this.cancellationTokenSource?.IsCancellationRequested == true; - - /// - public bool IsRunning => ExecutionTask?.IsCompleted == false; - - /// - public void NotifyCanExecuteChanged() - { - CanExecuteChanged?.Invoke(this, EventArgs.Empty); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanExecute(T? parameter) - { - return this.canExecute?.Invoke(parameter) != false; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanExecute(object? parameter) - { - if (default(T) is not null && - parameter is null) - { - return false; - } - - return CanExecute((T?)parameter); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Execute(T? parameter) - { - _ = ExecuteAsync(parameter); - } - - /// - public void Execute(object? parameter) - { - _ = ExecuteAsync((T?)parameter); - } - - /// - public Task ExecuteAsync(T? parameter) - { - if (CanExecute(parameter)) - { - // Non cancelable command delegate - if (this.execute is not null) - { - return ExecutionTask = this.execute(parameter); - } - - // Cancel the previous operation, if one is pending - this.cancellationTokenSource?.Cancel(); - - CancellationTokenSource cancellationTokenSource = this.cancellationTokenSource = new(); - - OnPropertyChanged(AsyncRelayCommand.IsCancellationRequestedChangedEventArgs); - - // Invoke the cancelable command delegate with a new linked token - return ExecutionTask = this.cancelableExecute!(parameter, cancellationTokenSource.Token); - } - - return Task.CompletedTask; - } - - /// - public Task ExecuteAsync(object? parameter) - { - return ExecuteAsync((T?)parameter); - } - - /// - public void Cancel() - { - this.cancellationTokenSource?.Cancel(); - - OnPropertyChanged(AsyncRelayCommand.IsCancellationRequestedChangedEventArgs); - OnPropertyChanged(AsyncRelayCommand.CanBeCanceledChangedEventArgs); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/Attributes/ICommandAttribute.cs b/Microsoft.Toolkit.Mvvm/Input/Attributes/ICommandAttribute.cs deleted file mode 100644 index 2a701d86b72..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/Attributes/ICommandAttribute.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Windows.Input; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// An attribute that can be used to automatically generate properties from declared methods. When this attribute - /// is used to decorate a method, a generator will create a command property with the corresponding interface - /// depending on the signature of the method. If an invalid method signature is used, the generator will report an error. - /// - /// In order to use this attribute, the containing type doesn't need to implement any interfaces. The generated properties will be lazily - /// assigned but their value will never change, so there is no need to support property change notifications or other additional functionality. - /// - /// - /// This attribute can be used as follows: - /// - /// partial class MyViewModel - /// { - /// [ICommand] - /// private void GreetUser(User? user) - /// { - /// Console.WriteLine($"Hello {user.Name}!"); - /// } - /// } - /// - /// And with this, code analogous to this will be generated: - /// - /// partial class MyViewModel - /// { - /// private IRelayCommand? greetUserCommand; - /// - /// public IRelayCommand GreetUserCommand => greetUserCommand ??= new RelayCommand(GreetUser); - /// } - /// - /// - /// - /// The following signatures are supported for annotated methods: - /// - /// void Method(); - /// - /// Will generate an property (using a instance). - /// - /// void Method(T?); - /// - /// Will generate an property (using a instance). - /// - /// Task Method(); - /// Task Method(CancellationToken); - /// - /// Will both generate an property (using an instance). - /// - /// Task Method(T?); - /// Task Method(T?, CancellationToken); - /// - /// Will both generate an property (using an instance). - /// - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class ICommandAttribute : Attribute - { - } -} diff --git a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IAsyncRelayCommand.cs b/Microsoft.Toolkit.Mvvm/Input/Interfaces/IAsyncRelayCommand.cs deleted file mode 100644 index 7af8823266e..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IAsyncRelayCommand.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// An interface expanding to support asynchronous operations. - /// - public interface IAsyncRelayCommand : IRelayCommand, INotifyPropertyChanged - { - /// - /// Gets the last scheduled , if available. - /// This property notifies a change when the completes. - /// - Task? ExecutionTask { get; } - - /// - /// Gets a value indicating whether running operations for this command can be canceled. - /// - bool CanBeCanceled { get; } - - /// - /// Gets a value indicating whether a cancelation request has been issued for the current operation. - /// - bool IsCancellationRequested { get; } - - /// - /// Gets a value indicating whether the command currently has a pending operation being executed. - /// - bool IsRunning { get; } - - /// - /// Provides a more specific version of , - /// also returning the representing the async operation being executed. - /// - /// The input parameter. - /// The representing the async operation being executed. - Task ExecuteAsync(object? parameter); - - /// - /// Communicates a request for cancelation. - /// - /// - /// If the underlying command is not running, or if it does not support cancelation, this method will perform no action. - /// Note that even with a successful cancelation, the completion of the current operation might not be immediate. - /// - void Cancel(); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IAsyncRelayCommand{T}.cs b/Microsoft.Toolkit.Mvvm/Input/Interfaces/IAsyncRelayCommand{T}.cs deleted file mode 100644 index 3cab9b2bb9f..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IAsyncRelayCommand{T}.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// A generic interface representing a more specific version of . - /// - /// The type used as argument for the interface methods. - /// This interface is needed to solve the diamond problem with base classes. - public interface IAsyncRelayCommand : IAsyncRelayCommand, IRelayCommand - { - /// - /// Provides a strongly-typed variant of . - /// - /// The input parameter. - /// The representing the async operation being executed. - Task ExecuteAsync(T? parameter); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IRelayCommand.cs b/Microsoft.Toolkit.Mvvm/Input/Interfaces/IRelayCommand.cs deleted file mode 100644 index 491db2211c5..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IRelayCommand.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Windows.Input; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// An interface expanding with the ability to raise - /// the event externally. - /// - public interface IRelayCommand : ICommand - { - /// - /// Notifies that the property has changed. - /// - void NotifyCanExecuteChanged(); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IRelayCommand{T}.cs b/Microsoft.Toolkit.Mvvm/Input/Interfaces/IRelayCommand{T}.cs deleted file mode 100644 index 209e3953718..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/Interfaces/IRelayCommand{T}.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Windows.Input; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// A generic interface representing a more specific version of . - /// - /// The type used as argument for the interface methods. - public interface IRelayCommand : IRelayCommand - { - /// - /// Provides a strongly-typed variant of . - /// - /// The input parameter. - /// Whether or not the current command can be executed. - /// Use this overload to avoid boxing, if is a value type. - bool CanExecute(T? parameter); - - /// - /// Provides a strongly-typed variant of . - /// - /// The input parameter. - /// Use this overload to avoid boxing, if is a value type. - void Execute(T? parameter); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/RelayCommand.cs b/Microsoft.Toolkit.Mvvm/Input/RelayCommand.cs deleted file mode 100644 index a702f2327c1..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/RelayCommand.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1512 - -// This file is inspired from the MvvmLight library (lbugnion/MvvmLight), -// more info in ThirdPartyNotices.txt in the root of the project. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// A command whose sole purpose is to relay its functionality to other - /// objects by invoking delegates. The default return value for the - /// method is . This type does not allow you to accept command parameters - /// in the and callback methods. - /// - public sealed class RelayCommand : IRelayCommand - { - /// - /// The to invoke when is used. - /// - private readonly Action execute; - - /// - /// The optional action to invoke when is used. - /// - private readonly Func? canExecute; - - /// - public event EventHandler? CanExecuteChanged; - - /// - /// Initializes a new instance of the class that can always execute. - /// - /// The execution logic. - public RelayCommand(Action execute) - { - this.execute = execute; - } - - /// - /// Initializes a new instance of the class. - /// - /// The execution logic. - /// The execution status logic. - public RelayCommand(Action execute, Func canExecute) - { - this.execute = execute; - this.canExecute = canExecute; - } - - /// - public void NotifyCanExecuteChanged() - { - CanExecuteChanged?.Invoke(this, EventArgs.Empty); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanExecute(object? parameter) - { - return this.canExecute?.Invoke() != false; - } - - /// - public void Execute(object? parameter) - { - if (CanExecute(parameter)) - { - this.execute(); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Input/RelayCommand{T}.cs b/Microsoft.Toolkit.Mvvm/Input/RelayCommand{T}.cs deleted file mode 100644 index f8bf29c263d..00000000000 --- a/Microsoft.Toolkit.Mvvm/Input/RelayCommand{T}.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1512 - -// This file is inspired from the MvvmLight library (lbugnion/MvvmLight), -// more info in ThirdPartyNotices.txt in the root of the project. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.Input -{ - /// - /// A generic command whose sole purpose is to relay its functionality to other - /// objects by invoking delegates. The default return value for the CanExecute - /// method is . This class allows you to accept command parameters - /// in the and callback methods. - /// - /// The type of parameter being passed as input to the callbacks. - public sealed class RelayCommand : IRelayCommand - { - /// - /// The to invoke when is used. - /// - private readonly Action execute; - - /// - /// The optional action to invoke when is used. - /// - private readonly Predicate? canExecute; - - /// - public event EventHandler? CanExecuteChanged; - - /// - /// Initializes a new instance of the class that can always execute. - /// - /// The execution logic. - /// - /// Due to the fact that the interface exposes methods that accept a - /// nullable parameter, it is recommended that if is a reference type, - /// you should always declare it as nullable, and to always perform checks within . - /// - public RelayCommand(Action execute) - { - this.execute = execute; - } - - /// - /// Initializes a new instance of the class. - /// - /// The execution logic. - /// The execution status logic. - /// See notes in . - public RelayCommand(Action execute, Predicate canExecute) - { - this.execute = execute; - this.canExecute = canExecute; - } - - /// - public void NotifyCanExecuteChanged() - { - CanExecuteChanged?.Invoke(this, EventArgs.Empty); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CanExecute(T? parameter) - { - return this.canExecute?.Invoke(parameter) != false; - } - - /// - public bool CanExecute(object? parameter) - { - if (default(T) is not null && - parameter is null) - { - return false; - } - - return CanExecute((T?)parameter); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Execute(T? parameter) - { - if (CanExecute(parameter)) - { - this.execute(parameter); - } - } - - /// - public void Execute(object? parameter) - { - Execute((T?)parameter); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/IMessenger.cs b/Microsoft.Toolkit.Mvvm/Messaging/IMessenger.cs deleted file mode 100644 index df1882c904d..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/IMessenger.cs +++ /dev/null @@ -1,163 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; - -namespace Microsoft.Toolkit.Mvvm.Messaging -{ - /// - /// A used to represent actions to invoke when a message is received. - /// The recipient is given as an input argument to allow message registrations to avoid creating - /// closures: if an instance method on a recipient needs to be invoked it is possible to just - /// cast the recipient to the right type and then access the local method from that instance. - /// - /// The type of recipient for the message. - /// The type of message to receive. - /// The recipient that is receiving the message. - /// The message being received. - public delegate void MessageHandler(TRecipient recipient, TMessage message) - where TRecipient : class - where TMessage : class; - - /// - /// An interface for a type providing the ability to exchange messages between different objects. - /// This can be useful to decouple different modules of an application without having to keep strong - /// references to types being referenced. It is also possible to send messages to specific channels, uniquely - /// identified by a token, and to have different messengers in different sections of an applications. - /// In order to use the functionalities, first define a message type, like so: - /// - /// public sealed class LoginCompletedMessage { } - /// - /// Then, register your a recipient for this message: - /// - /// Messenger.Default.Register<MyRecipientType, LoginCompletedMessage>(this, (r, m) => - /// { - /// // Handle the message here... - /// }); - /// - /// The message handler here is a lambda expression taking two parameters: the recipient and the message. - /// This is done to avoid the allocations for the closures that would've been generated if the expression - /// had captured the current instance. The recipient type parameter is used so that the recipient can be - /// directly accessed within the handler without the need to manually perform type casts. This allows the - /// code to be less verbose and more reliable, as all the checks are done just at build time. If the handler - /// is defined within the same type as the recipient, it is also possible to directly access private members. - /// This allows the message handler to be a static method, which enables the C# compiler to perform a number - /// of additional memory optimizations (such as caching the delegate, avoiding unnecessary memory allocations). - /// Finally, send a message when needed, like so: - /// - /// Messenger.Default.Send<LoginCompletedMessage>(); - /// - /// Additionally, the method group syntax can also be used to specify the message handler - /// to invoke when receiving a message, if a method with the right signature is available - /// in the current scope. This is helpful to keep the registration and handling logic separate. - /// Following up from the previous example, consider a class having this method: - /// - /// private static void Receive(MyRecipientType recipient, LoginCompletedMessage message) - /// { - /// // Handle the message there - /// } - /// - /// The registration can then be performed in a single line like so: - /// - /// Messenger.Default.Register(this, Receive); - /// - /// The C# compiler will automatically convert that expression to a instance - /// compatible with . - /// This will also work if multiple overloads of that method are available, each handling a different - /// message type: the C# compiler will automatically pick the right one for the current message type. - /// It is also possible to register message handlers explicitly using the interface. - /// To do so, the recipient just needs to implement the interface and then call the - /// extension, which will automatically register - /// all the handlers that are declared by the recipient type. Registration for individual handlers is supported as well. - /// - public interface IMessenger - { - /// - /// Checks whether or not a given recipient has already been registered for a message. - /// - /// The type of message to check for the given recipient. - /// The type of token to check the channel for. - /// The target recipient to check the registration for. - /// The token used to identify the target channel to check. - /// Whether or not has already been registered for the specified message. - [Pure] - bool IsRegistered(object recipient, TToken token) - where TMessage : class - where TToken : IEquatable; - - /// - /// Registers a recipient for a given type of message. - /// - /// The type of recipient for the message. - /// The type of message to receive. - /// The type of token to use to pick the messages to receive. - /// The recipient that will receive the messages. - /// A token used to determine the receiving channel to use. - /// The to invoke when a message is received. - /// Thrown when trying to register the same message twice. - void Register(TRecipient recipient, TToken token, MessageHandler handler) - where TRecipient : class - where TMessage : class - where TToken : IEquatable; - - /// - /// Unregisters a recipient from all registered messages. - /// - /// The recipient to unregister. - /// - /// This method will unregister the target recipient across all channels. - /// Use this method as an easy way to lose all references to a target recipient. - /// If the recipient has no registered handler, this method does nothing. - /// - void UnregisterAll(object recipient); - - /// - /// Unregisters a recipient from all messages on a specific channel. - /// - /// The type of token to identify what channel to unregister from. - /// The recipient to unregister. - /// The token to use to identify which handlers to unregister. - /// If the recipient has no registered handler, this method does nothing. - void UnregisterAll(object recipient, TToken token) - where TToken : IEquatable; - - /// - /// Unregisters a recipient from messages of a given type. - /// - /// The type of message to stop receiving. - /// The type of token to identify what channel to unregister from. - /// The recipient to unregister. - /// The token to use to identify which handlers to unregister. - /// If the recipient has no registered handler, this method does nothing. - void Unregister(object recipient, TToken token) - where TMessage : class - where TToken : IEquatable; - - /// - /// Sends a message of the specified type to all registered recipients. - /// - /// The type of message to send. - /// The type of token to identify what channel to use to send the message. - /// The message to send. - /// The token indicating what channel to use. - /// The message that was sent (ie. ). - TMessage Send(TMessage message, TToken token) - where TMessage : class - where TToken : IEquatable; - - /// - /// Performs a cleanup on the current messenger. - /// Invoking this method does not unregister any of the currently registered - /// recipient, and it can be used to perform cleanup operations such as - /// trimming the internal data structures of a messenger implementation. - /// - void Cleanup(); - - /// - /// Resets the instance and unregisters all the existing recipients. - /// - void Reset(); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/IMessengerExtensions.cs b/Microsoft.Toolkit.Mvvm/Messaging/IMessengerExtensions.cs deleted file mode 100644 index 2c8769f701a..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/IMessengerExtensions.cs +++ /dev/null @@ -1,364 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; -using Microsoft.Toolkit.Mvvm.Messaging.Internals; - -namespace Microsoft.Toolkit.Mvvm.Messaging -{ - /// - /// Extensions for the type. - /// - public static class IMessengerExtensions - { - /// - /// A class that acts as a container to load the instance linked to - /// the method. - /// This class is needed to avoid forcing the initialization code in the static constructor to run as soon as - /// the type is referenced, even if that is done just to use methods - /// that do not actually require this instance to be available. - /// We're effectively using this type to leverage the lazy loading of static constructors done by the runtime. - /// - private static class MethodInfos - { - /// - /// The instance associated with . - /// - public static readonly MethodInfo RegisterIRecipient = new Action, Unit>(Register).Method.GetGenericMethodDefinition(); - } - - /// - /// A non-generic version of . - /// - private static class DiscoveredRecipients - { - /// - /// The instance used to track the preloaded registration action for each recipient. - /// - public static readonly ConditionalWeakTable?> RegistrationMethods = new(); - } - - /// - /// A class that acts as a static container to associate a instance to each - /// type in use. This is done because we can only use a single type as key, but we need to track - /// associations of each recipient type also across different communication channels, each identified by a token. - /// Since the token is actually a compile-time parameter, we can use a wrapping class to let the runtime handle a different - /// instance for each generic type instantiation. This lets us only worry about the recipient type being inspected. - /// - /// The token indicating what channel to use. - private static class DiscoveredRecipients - where TToken : IEquatable - { - /// - /// The instance used to track the preloaded registration action for each recipient. - /// - public static readonly ConditionalWeakTable> RegistrationMethods = new(); - } - - /// - /// Checks whether or not a given recipient has already been registered for a message. - /// - /// The type of message to check for the given recipient. - /// The instance to use to check the registration. - /// The target recipient to check the registration for. - /// Whether or not has already been registered for the specified message. - /// This method will use the default channel to check for the requested registration. - [Pure] - public static bool IsRegistered(this IMessenger messenger, object recipient) - where TMessage : class - { - return messenger.IsRegistered(recipient, default); - } - - /// - /// Registers all declared message handlers for a given recipient, using the default channel. - /// - /// The instance to use to register the recipient. - /// The recipient that will receive the messages. - /// See notes for for more info. - public static void RegisterAll(this IMessenger messenger, object recipient) - { - // We use this method as a callback for the conditional weak table, which will handle - // thread-safety for us. This first callback will try to find a generated method for the - // target recipient type, and just invoke it to get the delegate to cache and use later. - static Action? LoadRegistrationMethodsForType(Type recipientType) - { - if (recipientType.Assembly.GetType("Microsoft.Toolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions") is Type extensionsType && - extensionsType.GetMethod("CreateAllMessagesRegistrator", new[] { recipientType }) is MethodInfo methodInfo) - { - return (Action)methodInfo.Invoke(null, new object?[] { null })!; - } - - return null; - } - - // Try to get the cached delegate, if the generatos has run correctly - Action? registrationAction = DiscoveredRecipients.RegistrationMethods.GetValue( - recipient.GetType(), - static t => LoadRegistrationMethodsForType(t)); - - if (registrationAction is not null) - { - registrationAction(messenger, recipient); - } - else - { - messenger.RegisterAll(recipient, default(Unit)); - } - } - - /// - /// Registers all declared message handlers for a given recipient. - /// - /// The type of token to identify what channel to use to receive messages. - /// The instance to use to register the recipient. - /// The recipient that will receive the messages. - /// The token indicating what channel to use. - /// - /// This method will register all messages corresponding to the interfaces - /// being implemented by . If none are present, this method will do nothing. - /// Note that unlike all other extensions, this method will use reflection to find the handlers to register. - /// Once the registration is complete though, the performance will be exactly the same as with handlers - /// registered directly through any of the other generic extensions for the interface. - /// - public static void RegisterAll(this IMessenger messenger, object recipient, TToken token) - where TToken : IEquatable - { - // We use this method as a callback for the conditional weak table, which will handle - // thread-safety for us. This first callback will try to find a generated method for the - // target recipient type, and just invoke it to get the delegate to cache and use later. - // In this case we also need to create a generic instantiation of the target method first. - static Action LoadRegistrationMethodsForType(Type recipientType) - { - if (recipientType.Assembly.GetType("Microsoft.Toolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions") is Type extensionsType && - extensionsType.GetMethod("CreateAllMessagesRegistratorWithToken", new[] { recipientType }) is MethodInfo methodInfo) - { - MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(typeof(TToken)); - - return (Action)genericMethodInfo.Invoke(null, new object?[] { null })!; - } - - return LoadRegistrationMethodsForTypeFallback(recipientType); - } - - // Fallback method when a generated method is not found. - // This method is only invoked once per recipient type and token type, so we're not - // worried about making it super efficient, and we can use the LINQ code for clarity. - // The LINQ codegen bloat is not really important for the same reason. - static Action LoadRegistrationMethodsForTypeFallback(Type recipientType) - { - // Get the collection of validation methods - MethodInfo[] registrationMethods = ( - from interfaceType in recipientType.GetInterfaces() - where interfaceType.IsGenericType && - interfaceType.GetGenericTypeDefinition() == typeof(IRecipient<>) - let messageType = interfaceType.GenericTypeArguments[0] - select MethodInfos.RegisterIRecipient.MakeGenericMethod(messageType, typeof(TToken))).ToArray(); - - // Short path if there are no message handlers to register - if (registrationMethods.Length == 0) - { - return static (_, _, _) => { }; - } - - // Input parameters (IMessenger instance, non-generic recipient, token) - ParameterExpression - arg0 = Expression.Parameter(typeof(IMessenger)), - arg1 = Expression.Parameter(typeof(object)), - arg2 = Expression.Parameter(typeof(TToken)); - - // Declare a local resulting from the (RecipientType)recipient cast - UnaryExpression inst1 = Expression.Convert(arg1, recipientType); - - // We want a single compiled LINQ expression that executes the registration for all - // the declared message types in the input type. To do so, we create a block with the - // unrolled invocations for the indivudual message registration (for each IRecipient). - // The code below will generate the following block expression: - // =============================================================================== - // { - // var inst1 = (RecipientType)arg1; - // IMessengerExtensions.Register(arg0, inst1, arg2); - // IMessengerExtensions.Register(arg0, inst1, arg2); - // ... - // IMessengerExtensions.Register(arg0, inst1, arg2); - // } - // =============================================================================== - // We also add an explicit object conversion to cast the input recipient type to - // the actual specific type, so that the exposed message handlers are accessible. - BlockExpression body = Expression.Block( - from registrationMethod in registrationMethods - select Expression.Call(registrationMethod, new Expression[] - { - arg0, - inst1, - arg2 - })); - - return Expression.Lambda>(body, arg0, arg1, arg2).Compile(); - } - - // Get or compute the registration method for the current recipient type. - // As in Microsoft.Toolkit.Diagnostics.TypeExtensions.ToTypeString, we use a lambda - // expression instead of a method group expression to leverage the statically initialized - // delegate and avoid repeated allocations for each invocation of this method. - // For more info on this, see the related issue at https://github.com/dotnet/roslyn/issues/5835. - Action registrationAction = DiscoveredRecipients.RegistrationMethods.GetValue( - recipient.GetType(), - static t => LoadRegistrationMethodsForType(t)); - - // Invoke the cached delegate to actually execute the message registration - registrationAction(messenger, recipient, token); - } - - /// - /// Registers a recipient for a given type of message. - /// - /// The type of message to receive. - /// The instance to use to register the recipient. - /// The recipient that will receive the messages. - /// Thrown when trying to register the same message twice. - /// This method will use the default channel to perform the requested registration. - public static void Register(this IMessenger messenger, IRecipient recipient) - where TMessage : class - { - messenger.Register, TMessage, Unit>(recipient, default, static (r, m) => r.Receive(m)); - } - - /// - /// Registers a recipient for a given type of message. - /// - /// The type of message to receive. - /// The type of token to identify what channel to use to receive messages. - /// The instance to use to register the recipient. - /// The recipient that will receive the messages. - /// The token indicating what channel to use. - /// Thrown when trying to register the same message twice. - /// This method will use the default channel to perform the requested registration. - public static void Register(this IMessenger messenger, IRecipient recipient, TToken token) - where TMessage : class - where TToken : IEquatable - { - messenger.Register, TMessage, TToken>(recipient, token, static (r, m) => r.Receive(m)); - } - - /// - /// Registers a recipient for a given type of message. - /// - /// The type of message to receive. - /// The instance to use to register the recipient. - /// The recipient that will receive the messages. - /// The to invoke when a message is received. - /// Thrown when trying to register the same message twice. - /// This method will use the default channel to perform the requested registration. - public static void Register(this IMessenger messenger, object recipient, MessageHandler handler) - where TMessage : class - { - messenger.Register(recipient, default(Unit), handler); - } - - /// - /// Registers a recipient for a given type of message. - /// - /// The type of recipient for the message. - /// The type of message to receive. - /// The instance to use to register the recipient. - /// The recipient that will receive the messages. - /// The to invoke when a message is received. - /// Thrown when trying to register the same message twice. - /// This method will use the default channel to perform the requested registration. - public static void Register(this IMessenger messenger, TRecipient recipient, MessageHandler handler) - where TRecipient : class - where TMessage : class - { - messenger.Register(recipient, default(Unit), handler); - } - - /// - /// Registers a recipient for a given type of message. - /// - /// The type of message to receive. - /// The type of token to use to pick the messages to receive. - /// The instance to use to register the recipient. - /// The recipient that will receive the messages. - /// A token used to determine the receiving channel to use. - /// The to invoke when a message is received. - /// Thrown when trying to register the same message twice. - public static void Register(this IMessenger messenger, object recipient, TToken token, MessageHandler handler) - where TMessage : class - where TToken : IEquatable - { - messenger.Register(recipient, token, handler); - } - - /// - /// Unregisters a recipient from messages of a given type. - /// - /// The type of message to stop receiving. - /// The instance to use to unregister the recipient. - /// The recipient to unregister. - /// - /// This method will unregister the target recipient only from the default channel. - /// If the recipient has no registered handler, this method does nothing. - /// - public static void Unregister(this IMessenger messenger, object recipient) - where TMessage : class - { - messenger.Unregister(recipient, default); - } - - /// - /// Sends a message of the specified type to all registered recipients. - /// - /// The type of message to send. - /// The instance to use to send the message. - /// The message that has been sent. - /// - /// This method is a shorthand for when the - /// message type exposes a parameterless constructor: it will automatically create - /// a new instance and send that to its recipients. - /// - public static TMessage Send(this IMessenger messenger) - where TMessage : class, new() - { - return messenger.Send(new TMessage(), default(Unit)); - } - - /// - /// Sends a message of the specified type to all registered recipients. - /// - /// The type of message to send. - /// The instance to use to send the message. - /// The message to send. - /// The message that was sent (ie. ). - public static TMessage Send(this IMessenger messenger, TMessage message) - where TMessage : class - { - return messenger.Send(message, default(Unit)); - } - - /// - /// Sends a message of the specified type to all registered recipients. - /// - /// The type of message to send. - /// The type of token to identify what channel to use to send the message. - /// The instance to use to send the message. - /// The token indicating what channel to use. - /// The message that has been sen. - /// - /// This method will automatically create a new instance - /// just like , and then send it to the right recipients. - /// - public static TMessage Send(this IMessenger messenger, TToken token) - where TMessage : class, new() - where TToken : IEquatable - { - return messenger.Send(new TMessage(), token); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/IRecipient{TMessage}.cs b/Microsoft.Toolkit.Mvvm/Messaging/IRecipient{TMessage}.cs deleted file mode 100644 index e6a5909415c..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/IRecipient{TMessage}.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.Mvvm.Messaging -{ - /// - /// An interface for a recipient that declares a registration for a specific message type. - /// - /// The type of message to receive. - public interface IRecipient - where TMessage : class - { - /// - /// Receives a given message instance. - /// - /// The message being received. - void Receive(TMessage message); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/ArrayPoolBufferWriter{T}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/ArrayPoolBufferWriter{T}.cs deleted file mode 100644 index 4cff9ae3122..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/ArrayPoolBufferWriter{T}.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.Messaging.Internals -{ - /// - /// A simple buffer writer implementation using pooled arrays. - /// - /// The type of items to store in the list. - /// - /// This type is a to avoid the object allocation and to - /// enable the pattern-based support. We aren't worried with consumers not - /// using this type correctly since it's private and only accessible within the parent type. - /// - internal ref struct ArrayPoolBufferWriter - { - /// - /// The default buffer size to use to expand empty arrays. - /// - private const int DefaultInitialBufferSize = 128; - - /// - /// The underlying array. - /// - private T[] array; - - /// - /// The starting offset within . - /// - private int index; - - /// - /// Creates a new instance of the struct. - /// - /// A new instance. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ArrayPoolBufferWriter Create() - { - return new() { array = ArrayPool.Shared.Rent(DefaultInitialBufferSize) }; - } - - /// - /// Gets a with the current items. - /// - public ReadOnlySpan Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.array.AsSpan(0, this.index); - } - - /// - /// Adds a new item to the current collection. - /// - /// The item to add. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(T item) - { - if (this.index == this.array.Length) - { - ResizeBuffer(); - } - - this.array[this.index++] = item; - } - - /// - /// Resets the underlying array and the stored items. - /// - public void Reset() - { - Array.Clear(this.array, 0, this.index); - - this.index = 0; - } - - /// - /// Resizes when there is no space left for new items. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private void ResizeBuffer() - { - T[] rent = ArrayPool.Shared.Rent(this.index << 2); - - Array.Copy(this.array, 0, rent, 0, this.index); - Array.Clear(this.array, 0, this.index); - - ArrayPool.Shared.Return(this.array); - - this.array = rent; - } - - /// - public void Dispose() - { - Array.Clear(this.array, 0, this.index); - - ArrayPool.Shared.Return(this.array); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/ConditionalWeakTable2{TKey,TValue}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/ConditionalWeakTable2{TKey,TValue}.cs deleted file mode 100644 index 9a5b0acfe32..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/ConditionalWeakTable2{TKey,TValue}.cs +++ /dev/null @@ -1,165 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if NETSTANDARD2_0 - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.Messaging.Internals -{ - /// - /// A wrapper for - /// that backports the enumerable support to .NET Standard 2.0 through an auxiliary list. - /// - /// Tke key of items to store in the table. - /// The values to store in the table. - internal sealed class ConditionalWeakTable2 - where TKey : class - where TValue : class? - { - /// - /// The underlying instance. - /// - private readonly ConditionalWeakTable table = new(); - - /// - /// A supporting linked list to store keys in . This is needed to expose - /// the ability to enumerate existing keys when there is no support for that in the BCL. - /// - private readonly LinkedList> keys = new(); - - /// - public bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value) - { - return this.table.TryGetValue(key, out value); - } - - /// - public TValue GetValue(TKey key, ConditionalWeakTable.CreateValueCallback createValueCallback) - { - // Get or create the value. When this method returns, the key will be present in the table - TValue value = this.table.GetValue(key, createValueCallback); - - // Check if the list of keys contains the given key. - // If it does, we can just stop here and return the result. - foreach (WeakReference node in this.keys) - { - if (node.TryGetTarget(out TKey? target) && - ReferenceEquals(target, key)) - { - return value; - } - } - - // Add the key to the list of weak references to track it - this.keys.AddFirst(new WeakReference(key)); - - return value; - } - - /// - public bool Remove(TKey key) - { - return this.table.Remove(key); - } - - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new(this); - - /// - /// A custom enumerator that traverses items in a instance. - /// - public ref struct Enumerator - { - /// - /// The owner instance for the enumerator. - /// - private readonly ConditionalWeakTable2 owner; - - /// - /// The current , if any. - /// - private LinkedListNode>? node; - - /// - /// The current to return. - /// - private KeyValuePair current; - - /// - /// Indicates whether or not has been called at least once. - /// - private bool isFirstMoveNextPending; - - /// - /// Initializes a new instance of the struct. - /// - /// The owner instance for the enumerator. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator(ConditionalWeakTable2 owner) - { - this.owner = owner; - this.node = null; - this.current = default; - this.isFirstMoveNextPending = true; - } - - /// - public bool MoveNext() - { - LinkedListNode>? node; - - if (!isFirstMoveNextPending) - { - node = this.node!.Next; - } - else - { - node = this.owner.keys.First; - - this.isFirstMoveNextPending = false; - } - - while (node is not null) - { - LinkedListNode>? nextNode = node.Next; - - // Get the key and value for the current node - if (node.Value.TryGetTarget(out TKey? target) && - this.owner.table.TryGetValue(target!, out TValue? value)) - { - this.node = node; - this.current = new KeyValuePair(target, value); - - return true; - } - else - { - // If the current key has been collected, trim the list - this.owner.keys.Remove(node); - } - - node = nextNode; - } - - return false; - } - - /// - public readonly KeyValuePair Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.current; - } - } - } -} - -#endif \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/DictionarySlim{TKey,TValue}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/DictionarySlim{TKey,TValue}.cs deleted file mode 100644 index 3c8d0e8b057..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/DictionarySlim{TKey,TValue}.cs +++ /dev/null @@ -1,407 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1512 - -// The DictionarySlim type is originally from CoreFX labs, see -// the source repository on GitHub at https://github.com/dotnet/corefxlab. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Collections.Extensions -{ - /// - /// A lightweight Dictionary with three principal differences compared to - /// - /// 1) It is possible to do "get or add" in a single lookup. For value types, this also saves a copy of the value. - /// 2) It assumes it is cheap to equate values. - /// 3) It assumes the keys implement and they are cheap and sufficient. - /// - /// The type of keys in the dictionary. - /// The type of values in the dictionary. - /// - /// 1) This avoids having to do separate lookups ( - /// followed by . - /// There is not currently an API exposed to get a value by ref without adding if the key is not present. - /// 2) This means it can save space by not storing hash codes. - /// 3) This means it can avoid storing a comparer, and avoid the likely virtual call to a comparer. - /// - [DebuggerDisplay("Count = {Count}")] - internal class DictionarySlim : IDictionarySlim - where TKey : IEquatable - where TValue : class - { - /// - /// A reusable array of items with a single value. - /// This is used when a new instance is - /// created, or when one is cleared. The first item being added to this collection - /// will immediately cause the first resize (see for more info). - /// - private static readonly Entry[] InitialEntries = new Entry[1]; - - /// - /// The current number of items stored in the map. - /// - private int count; - - /// - /// The 1-based index for the start of the free list within . - /// - private int freeList = -1; - - /// - /// The array of 1-based indices for the items stored in . - /// - private int[] buckets; - - /// - /// The array of currently stored key-value pairs (ie. the lists for each hash group). - /// - private Entry[] entries; - - /// - /// A type representing a map entry, ie. a node in a given list. - /// - private struct Entry - { - /// - /// The key for the value in the current node. - /// - public TKey Key; - - /// - /// The value in the current node, if present. - /// - public TValue? Value; - - /// - /// The 0-based index for the next node in the current list. - /// - public int Next; - } - - /// - /// Initializes a new instance of the class. - /// - public DictionarySlim() - { - this.buckets = HashHelpers.SizeOneIntArray; - this.entries = InitialEntries; - } - - /// - public int Count => this.count; - - /// - public TValue this[TKey key] - { - get - { - Entry[] entries = this.entries; - - for (int i = this.buckets[key.GetHashCode() & (this.buckets.Length - 1)] - 1; - (uint)i < (uint)entries.Length; - i = entries[i].Next) - { - if (key.Equals(entries[i].Key)) - { - return entries[i].Value!; - } - } - - ThrowArgumentExceptionForKeyNotFound(key); - - return default!; - } - } - - /// - public void Clear() - { - this.count = 0; - this.freeList = -1; - this.buckets = HashHelpers.SizeOneIntArray; - this.entries = InitialEntries; - } - - /// - /// Checks whether or not the dictionary contains a pair with a specified key. - /// - /// The key to look for. - /// Whether or not the key was present in the dictionary. - public bool ContainsKey(TKey key) - { - Entry[] entries = this.entries; - - for (int i = this.buckets[key.GetHashCode() & (this.buckets.Length - 1)] - 1; - (uint)i < (uint)entries.Length; - i = entries[i].Next) - { - if (key.Equals(entries[i].Key)) - { - return true; - } - } - - return false; - } - - /// - /// Gets the value if present for the specified key. - /// - /// The key to look for. - /// The value found, otherwise . - /// Whether or not the key was present. - public bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value) - { - Entry[] entries = this.entries; - - for (int i = this.buckets[key.GetHashCode() & (this.buckets.Length - 1)] - 1; - (uint)i < (uint)entries.Length; - i = entries[i].Next) - { - if (key.Equals(entries[i].Key)) - { - value = entries[i].Value!; - - return true; - } - } - - value = default!; - - return false; - } - - /// - public bool TryRemove(TKey key) - { - Entry[] entries = this.entries; - int bucketIndex = key.GetHashCode() & (this.buckets.Length - 1); - int entryIndex = this.buckets[bucketIndex] - 1; - int lastIndex = -1; - - while (entryIndex != -1) - { - Entry candidate = entries[entryIndex]; - - if (candidate.Key.Equals(key)) - { - if (lastIndex != -1) - { - entries[lastIndex].Next = candidate.Next; - } - else - { - this.buckets[bucketIndex] = candidate.Next + 1; - } - - entries[entryIndex] = default; - entries[entryIndex].Next = -3 - this.freeList; - - this.freeList = entryIndex; - this.count--; - - return true; - } - - lastIndex = entryIndex; - entryIndex = candidate.Next; - } - - return false; - } - - /// - /// Gets the value for the specified key, or, if the key is not present, - /// adds an entry and returns the value by ref. This makes it possible to - /// add or update a value in a single look up operation. - /// - /// Key to look for - /// Reference to the new or existing value - public ref TValue? GetOrAddValueRef(TKey key) - { - Entry[] entries = this.entries; - int bucketIndex = key.GetHashCode() & (this.buckets.Length - 1); - - for (int i = this.buckets[bucketIndex] - 1; - (uint)i < (uint)entries.Length; - i = entries[i].Next) - { - if (key.Equals(entries[i].Key)) - { - return ref entries[i].Value; - } - } - - return ref AddKey(key, bucketIndex); - } - - /// - /// Creates a slot for a new value to add for a specified key. - /// - /// The key to use to add the new value. - /// The target bucked index to use. - /// A reference to the slot for the new value to add. - [MethodImpl(MethodImplOptions.NoInlining)] - private ref TValue? AddKey(TKey key, int bucketIndex) - { - Entry[] entries = this.entries; - int entryIndex; - - if (this.freeList != -1) - { - entryIndex = this.freeList; - - this.freeList = -3 - entries[this.freeList].Next; - } - else - { - if (this.count == entries.Length || entries.Length == 1) - { - entries = Resize(); - bucketIndex = key.GetHashCode() & (this.buckets.Length - 1); - } - - entryIndex = this.count; - } - - entries[entryIndex].Key = key; - entries[entryIndex].Next = this.buckets[bucketIndex] - 1; - - this.buckets[bucketIndex] = entryIndex + 1; - this.count++; - - return ref entries[entryIndex].Value; - } - - /// - /// Resizes the current dictionary to reduce the number of collisions - /// - [MethodImpl(MethodImplOptions.NoInlining)] - private Entry[] Resize() - { - int count = this.count; - int newSize = this.entries.Length * 2; - - if ((uint)newSize > int.MaxValue) - { - ThrowInvalidOperationExceptionForMaxCapacityExceeded(); - } - - var entries = new Entry[newSize]; - - Array.Copy(this.entries, 0, entries, 0, count); - - var newBuckets = new int[entries.Length]; - - while (count-- > 0) - { - int bucketIndex = entries[count].Key.GetHashCode() & (newBuckets.Length - 1); - - entries[count].Next = newBuckets[bucketIndex] - 1; - - newBuckets[bucketIndex] = count + 1; - } - - this.buckets = newBuckets; - this.entries = entries; - - return entries; - } - - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Enumerator GetEnumerator() => new(this); - - /// - /// Enumerator for . - /// - public ref struct Enumerator - { - private readonly Entry[] entries; - private int index; - private int count; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(DictionarySlim dictionary) - { - this.entries = dictionary.entries; - this.index = 0; - this.count = dictionary.count; - } - - /// - public bool MoveNext() - { - if (this.count == 0) - { - return false; - } - - this.count--; - - Entry[] entries = this.entries; - - while (entries[this.index].Next < -1) - { - this.index++; - } - - // We need to preemptively increment the current index so that we still correctly keep track - // of the current position in the dictionary even if the users doesn't access any of the - // available properties in the enumerator. As this is a possibility, we can't rely on one of - // them to increment the index before MoveNext is invoked again. We ditch the standard enumerator - // API surface here to expose the Key/Value properties directly and minimize the memory copies. - // For the same reason, we also removed the KeyValuePair field here, and instead - // rely on the properties lazily accessing the target instances directly from the current entry - // pointed at by the index property (adjusted backwards to account for the increment here). - this.index++; - - return true; - } - - /// - /// Gets the current key. - /// - public TKey Key - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.entries[this.index - 1].Key; - } - - /// - /// Gets the current value. - /// - public TValue Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.entries[this.index - 1].Value!; - } - } - - /// - /// Throws an when trying to load an element with a missing key. - /// - private static void ThrowArgumentExceptionForKeyNotFound(TKey key) - { - throw new ArgumentException($"The target key {key} was not present in the dictionary"); - } - - /// - /// Throws an when trying to resize over the maximum capacity. - /// - private static void ThrowInvalidOperationExceptionForMaxCapacityExceeded() - { - throw new InvalidOperationException("Max capacity exceeded"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/HashHelpers.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/HashHelpers.cs deleted file mode 100644 index 3062f15ce7f..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/HashHelpers.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Collections.Extensions -{ - /// - /// A helper class for . - /// - internal static class HashHelpers - { - /// - /// An array of type of size 1. - /// - public static readonly int[] SizeOneIntArray = new int[1]; - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim.cs deleted file mode 100644 index 16c7571cd1d..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Collections.Extensions -{ - /// - /// A base interface masking instances and exposing non-generic functionalities. - /// - internal interface IDictionarySlim - { - /// - /// Gets the count of entries in the dictionary. - /// - int Count { get; } - - /// - /// Clears the current dictionary. - /// - void Clear(); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim{TKey,TValue}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim{TKey,TValue}.cs deleted file mode 100644 index 011cf98a938..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim{TKey,TValue}.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Collections.Extensions -{ - /// - /// An interface providing key type contravariant and value type covariant access - /// to a instance. - /// - /// The contravariant type of keys in the dictionary. - /// The covariant type of values in the dictionary. - internal interface IDictionarySlim : IDictionarySlim - where TKey : IEquatable - where TValue : class - { - /// - /// Gets the value with the specified key. - /// - /// The key to look for. - /// The returned value. - /// Thrown if the key wasn't present. - TValue this[TKey key] { get; } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim{TKey}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim{TKey}.cs deleted file mode 100644 index edbae01b6b1..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Microsoft.Collections.Extensions/IDictionarySlim{TKey}.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.Collections.Extensions -{ - /// - /// An interface providing key type contravariant access to a instance. - /// - /// The contravariant type of keys in the dictionary. - internal interface IDictionarySlim : IDictionarySlim - where TKey : IEquatable - { - /// - /// Tries to remove a value with a specified key, if present. - /// - /// The key of the value to remove. - /// Whether or not the key was present. - bool TryRemove(TKey key); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/System/Gen2GcCallback.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/System/Gen2GcCallback.cs deleted file mode 100644 index 649de08eff9..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/System/Gen2GcCallback.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Runtime.ConstrainedExecution; -using System.Runtime.InteropServices; - -namespace System -{ - /// - /// Schedules a callback roughly every gen 2 GC (you may see a Gen 0 an Gen 1 but only once). - /// Ported from https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Gen2GcCallback.cs. - /// - internal sealed class Gen2GcCallback : CriticalFinalizerObject - { - /// - /// The callback to invoke at each GC. - /// - private readonly Action callback; - - /// - /// A weak to the target object to pass to . - /// - private GCHandle handle; - - /// - /// Initializes a new instance of the class. - /// - /// The callback to invoke at each GC. - /// The target object to pass as argument to . - private Gen2GcCallback(Action callback, object target) - { - this.callback = callback; - this.handle = GCHandle.Alloc(target, GCHandleType.Weak); - } - - /// - /// Schedules a callback to be called on each GC until the target is collected. - /// - /// The callback to invoke at each GC. - /// The target object to pass as argument to . - public static void Register(Action callback, object target) - { -#if NETSTANDARD2_0 - if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework")) - { - // On .NET Framework using a GC callback causes issues with app domain unloading, - // so the callback is not registered if that runtime is detected and just ignored. - // Users on .NET Framework will have to manually trim the messenger, if they'd like. - return; - } -#endif - - _ = new Gen2GcCallback(callback, target); - } - - /// - /// Finalizes an instance of the class. - /// This finalizer is re-registered with as long as - /// the target object is alive, which means it will be executed again every time a generation 2 - /// collection is triggered (as the instance itself would be moved to - /// that generation after surviving the generation 0 and 1 collections the first time). - /// - ~Gen2GcCallback() - { - if (this.handle.Target is object target) - { - try - { - this.callback(target); - } - catch - { - } - - GC.ReRegisterForFinalize(this); - } - else - { - handle.Free(); - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Type2.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/Type2.cs deleted file mode 100644 index 59126181114..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Type2.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.Messaging.Internals -{ - /// - /// A simple type representing an immutable pair of types. - /// - /// - /// This type replaces a simple as it's faster in its - /// and methods, and because - /// unlike a value tuple it exposes its fields as immutable. Additionally, the - /// and fields provide additional clarity reading - /// the code compared to and . - /// - internal readonly struct Type2 : IEquatable - { - /// - /// The type of registered message. - /// - public readonly Type TMessage; - - /// - /// The type of registration token. - /// - public readonly Type TToken; - - /// - /// Initializes a new instance of the struct. - /// - /// The type of registered message. - /// The type of registration token. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Type2(Type tMessage, Type tToken) - { - TMessage = tMessage; - TToken = tToken; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Type2 other) - { - // We can't just use reference equality, as that's technically not guaranteed - // to work and might fail in very rare cases (eg. with type forwarding between - // different assemblies). Instead, we can use the == operator to compare for - // equality, which still avoids the callvirt overhead of calling Type.Equals, - // and is also implemented as a JIT intrinsic on runtimes such as .NET Core. - return - TMessage == other.TMessage && - TToken == other.TToken; - } - - /// - public override bool Equals(object? obj) - { - return obj is Type2 other && Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - // To combine the two hashes, we can simply use the fast djb2 hash algorithm. Unfortunately we - // can't really skip the callvirt here (eg. by using RuntimeHelpers.GetHashCode like in other - // cases), as there are some niche cases mentioned above that might break when doing so. - // However since this method is not generally used in a hot path (eg. the message broadcasting - // only invokes this a handful of times when initially retrieving the target mapping), this - // doesn't actually make a noticeable difference despite the minor overhead of the virtual call. - int hash = TMessage.GetHashCode(); - - hash = (hash << 5) + hash; - - hash += TToken.GetHashCode(); - - return hash; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Unit.cs b/Microsoft.Toolkit.Mvvm/Messaging/Internals/Unit.cs deleted file mode 100644 index a0f5d7ea7ee..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Internals/Unit.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.Messaging.Internals -{ - /// - /// An empty type representing a generic token with no specific value. - /// - internal readonly struct Unit : IEquatable - { - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Unit other) - { - return true; - } - - /// - public override bool Equals(object? obj) - { - return obj is Unit; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return 0; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Messages/AsyncCollectionRequestMessage{T}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Messages/AsyncCollectionRequestMessage{T}.cs deleted file mode 100644 index f31317f95b6..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Messages/AsyncCollectionRequestMessage{T}.cs +++ /dev/null @@ -1,143 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Mvvm.Messaging.Messages -{ - /// - /// A for request messages that can receive multiple replies, which can either be used directly or through derived classes. - /// - /// The type of request to make. - public class AsyncCollectionRequestMessage : IAsyncEnumerable - { - /// - /// The collection of received replies. We accept both instance, representing already running - /// operations that can be executed in parallel, or instances, which can be used so that multiple - /// asynchronous operations are only started sequentially from and do not overlap in time. - /// - private readonly List<(Task?, Func>?)> responses = new(); - - /// - /// The instance used to link the token passed to - /// and the one passed to all subscribers to the message. - /// - private readonly CancellationTokenSource cancellationTokenSource = new(); - - /// - /// Gets the instance that will be linked to the - /// one used to asynchronously enumerate the received responses. This can be used to cancel asynchronous - /// replies that are still being processed, if no new items are needed from this request message. - /// Consider the following example, where we define a message to retrieve the currently opened documents: - /// - /// public class OpenDocumentsRequestMessage : AsyncCollectionRequestMessage<XmlDocument> { } - /// - /// We can then request and enumerate the results like so: - /// - /// await foreach (var document in Messenger.Default.Send<OpenDocumentsRequestMessage>()) - /// { - /// // Process each document here... - /// } - /// - /// If we also want to control the cancellation of the token passed to each subscriber to the message, - /// we can do so by passing a token we control to the returned message before starting the enumeration - /// (). - /// The previous snippet with this additional change looks as follows: - /// - /// await foreach (var document in Messenger.Default.Send<OpenDocumentsRequestMessage>().WithCancellation(cts.Token)) - /// { - /// // Process each document here... - /// } - /// - /// When no more new items are needed (or for any other reason depending on the situation), the token - /// passed to the enumerator can be canceled (by calling ), - /// and that will also notify the remaining tasks in the request message. The token exposed by the message - /// itself will automatically be linked and canceled with the one passed to the enumerator. - /// - public CancellationToken CancellationToken => this.cancellationTokenSource.Token; - - /// - /// Replies to the current request message. - /// - /// The response to use to reply to the request message. - public void Reply(T response) - { - Reply(Task.FromResult(response)); - } - - /// - /// Replies to the current request message. - /// - /// The response to use to reply to the request message. - public void Reply(Task response) - { - this.responses.Add((response, null)); - } - - /// - /// Replies to the current request message. - /// - /// The response to use to reply to the request message. - public void Reply(Func> response) - { - this.responses.Add((null, response)); - } - - /// - /// Gets the collection of received response items. - /// - /// A value to stop the operation. - /// The collection of received response items. - [Pure] - public async Task> GetResponsesAsync(CancellationToken cancellationToken = default) - { - if (cancellationToken.CanBeCanceled) - { - _ = cancellationToken.Register(this.cancellationTokenSource.Cancel); - } - - List results = new(this.responses.Count); - - await foreach (var response in this.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - results.Add(response); - } - - return results; - } - - /// - [Pure] - [EditorBrowsable(EditorBrowsableState.Never)] - public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) - { - if (cancellationToken.CanBeCanceled) - { - _ = cancellationToken.Register(this.cancellationTokenSource.Cancel); - } - - foreach (var (task, func) in this.responses) - { - if (cancellationToken.IsCancellationRequested) - { - yield break; - } - - if (task is not null) - { - yield return await task.ConfigureAwait(false); - } - else - { - yield return await func!(cancellationToken).ConfigureAwait(false); - } - } - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Messages/AsyncRequestMessage{T}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Messages/AsyncRequestMessage{T}.cs deleted file mode 100644 index a857b0ce774..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Messages/AsyncRequestMessage{T}.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Mvvm.Messaging.Messages -{ - /// - /// A for async request messages, which can either be used directly or through derived classes. - /// - /// The type of request to make. - public class AsyncRequestMessage - { - private Task? response; - - /// - /// Gets the message response. - /// - /// Thrown when is . - public Task Response - { - get - { - if (!HasReceivedResponse) - { - ThrowInvalidOperationExceptionForNoResponseReceived(); - } - - return this.response!; - } - } - - /// - /// Gets a value indicating whether a response has already been assigned to this instance. - /// - public bool HasReceivedResponse { get; private set; } - - /// - /// Replies to the current request message. - /// - /// The response to use to reply to the request message. - /// Thrown if has already been set. - public void Reply(T response) - { - Reply(Task.FromResult(response)); - } - - /// - /// Replies to the current request message. - /// - /// The response to use to reply to the request message. - /// Thrown if has already been set. - public void Reply(Task response) - { - if (HasReceivedResponse) - { - ThrowInvalidOperationExceptionForDuplicateReply(); - } - - HasReceivedResponse = true; - - this.response = response; - } - - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [EditorBrowsable(EditorBrowsableState.Never)] - public TaskAwaiter GetAwaiter() - { - return this.Response.GetAwaiter(); - } - - /// - /// Throws an when a response is not available. - /// - private static void ThrowInvalidOperationExceptionForNoResponseReceived() - { - throw new InvalidOperationException("No response was received for the given request message"); - } - - /// - /// Throws an when or are called twice. - /// - private static void ThrowInvalidOperationExceptionForDuplicateReply() - { - throw new InvalidOperationException("A response has already been issued for the current message"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Messages/CollectionRequestMessage{T}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Messages/CollectionRequestMessage{T}.cs deleted file mode 100644 index 2f4e95308b8..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Messages/CollectionRequestMessage{T}.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; - -namespace Microsoft.Toolkit.Mvvm.Messaging.Messages -{ - /// - /// A for request messages that can receive multiple replies, which can either be used directly or through derived classes. - /// - /// The type of request to make. - public class CollectionRequestMessage : IEnumerable - { - private readonly List responses = new(); - - /// - /// Gets the message responses. - /// - public IReadOnlyCollection Responses => this.responses; - - /// - /// Replies to the current request message. - /// - /// The response to use to reply to the request message. - public void Reply(T response) - { - this.responses.Add(response); - } - - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [EditorBrowsable(EditorBrowsableState.Never)] - public IEnumerator GetEnumerator() - { - return this.responses.GetEnumerator(); - } - - /// - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Messages/PropertyChangedMessage{T}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Messages/PropertyChangedMessage{T}.cs deleted file mode 100644 index 075ed306a00..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Messages/PropertyChangedMessage{T}.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#pragma warning disable SA1512 - -// This file is inspired from the MvvmLight library (lbugnion/MvvmLight), -// more info in ThirdPartyNotices.txt in the root of the project. - -namespace Microsoft.Toolkit.Mvvm.Messaging.Messages -{ - /// - /// A message used to broadcast property changes in observable objects. - /// - /// The type of the property to broadcast the change for. - public class PropertyChangedMessage - { - /// - /// Initializes a new instance of the class. - /// - /// The original sender of the broadcast message. - /// The name of the property that changed. - /// The value that the property had before the change. - /// The value that the property has after the change. - public PropertyChangedMessage(object sender, string? propertyName, T oldValue, T newValue) - { - Sender = sender; - PropertyName = propertyName; - OldValue = oldValue; - NewValue = newValue; - } - - /// - /// Gets the original sender of the broadcast message. - /// - public object Sender { get; } - - /// - /// Gets the name of the property that changed. - /// - public string? PropertyName { get; } - - /// - /// Gets the value that the property had before the change. - /// - public T OldValue { get; } - - /// - /// Gets the value that the property has after the change. - /// - public T NewValue { get; } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Messages/RequestMessage{T}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Messages/RequestMessage{T}.cs deleted file mode 100644 index 0a6ee08f2af..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Messages/RequestMessage{T}.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -#pragma warning disable CS8618 - -namespace Microsoft.Toolkit.Mvvm.Messaging.Messages -{ - /// - /// A for request messages, which can either be used directly or through derived classes. - /// - /// The type of request to make. - public class RequestMessage - { - private T response; - - /// - /// Gets the message response. - /// - /// Thrown when is . - public T Response - { - get - { - if (!HasReceivedResponse) - { - ThrowInvalidOperationExceptionForNoResponseReceived(); - } - - return this.response; - } - } - - /// - /// Gets a value indicating whether a response has already been assigned to this instance. - /// - public bool HasReceivedResponse { get; private set; } - - /// - /// Replies to the current request message. - /// - /// The response to use to reply to the request message. - /// Thrown if has already been set. - public void Reply(T response) - { - if (HasReceivedResponse) - { - ThrowInvalidOperationExceptionForDuplicateReply(); - } - - HasReceivedResponse = true; - - this.response = response; - } - - /// - /// Implicitly gets the response from a given instance. - /// - /// The input instance. - /// Thrown when is . - public static implicit operator T(RequestMessage message) - { - return message.Response; - } - - /// - /// Throws an when a response is not available. - /// - private static void ThrowInvalidOperationExceptionForNoResponseReceived() - { - throw new InvalidOperationException("No response was received for the given request message"); - } - - /// - /// Throws an when is called twice. - /// - private static void ThrowInvalidOperationExceptionForDuplicateReply() - { - throw new InvalidOperationException("A response has already been issued for the current message"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/Messages/ValueChangedMessage{T}.cs b/Microsoft.Toolkit.Mvvm/Messaging/Messages/ValueChangedMessage{T}.cs deleted file mode 100644 index 5d0ce62c916..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/Messages/ValueChangedMessage{T}.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.Mvvm.Messaging.Messages -{ - /// - /// A base message that signals whenever a specific value has changed. - /// - /// The type of value that has changed. - public class ValueChangedMessage - { - /// - /// Initializes a new instance of the class. - /// - /// The value that has changed. - public ValueChangedMessage(T value) - { - Value = value; - } - - /// - /// Gets the value that has changed. - /// - public T Value { get; } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/StrongReferenceMessenger.cs b/Microsoft.Toolkit.Mvvm/Messaging/StrongReferenceMessenger.cs deleted file mode 100644 index cbaa2799b85..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/StrongReferenceMessenger.cs +++ /dev/null @@ -1,578 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Threading; -using Microsoft.Collections.Extensions; -using Microsoft.Toolkit.Mvvm.Messaging.Internals; - -namespace Microsoft.Toolkit.Mvvm.Messaging -{ - /// - /// A class providing a reference implementation for the interface. - /// - /// - /// This implementation uses strong references to track the registered - /// recipients, so it is necessary to manually unregister them when they're no longer needed. - /// - public sealed class StrongReferenceMessenger : IMessenger - { - // This messenger uses the following logic to link stored instances together: - // -------------------------------------------------------------------------------------------------------- - // DictionarySlim> recipientsMap; - // | \________________[*]IDictionarySlim> - // | \____________/_________ / - // | ________(recipients registrations)____________________/ \ / - // | / ____(channel registrations)________________\____________/ - // | / / \ - // DictionarySlim>> mapping = Mapping - // / / / - // ___(Type2.TToken)____/ / / - // /________________(Type2.TMessage)________________________/ / - // / ____________________________________________________________/ - // / / - // DictionarySlim typesMap; - // -------------------------------------------------------------------------------------------------------- - // Each combination of results in a concrete Mapping type, which holds the references - // from registered recipients to handlers. The handlers are stored in a > dictionary, - // so that each recipient can have up to one registered handler for a given token, for each message type. - // Note that the registered handlers are only stored as object references, even if they were actually of type - // MessageHandler, to avoid unnecessary unsafe casts. Each handler is also generic with respect to the - // recipient type, in order to allow the messenger to track and invoke type-specific handlers without using reflection and - // without having to capture the input handler in a proxy delegate, causing one extra memory allocations and adding overhead. - // This allows users to retain type information on each registered recipient, instead of having to manually cast each recipient - // to the right type within the handler. The type conversion is guaranteed to be respected due to how the messenger type - // itself works - as registered handlers are always invoked on their respective recipients. - // Each mapping is stored in the types map, which associates each pair of concrete types to its - // mapping instance. Mapping instances are exposed as IMapping items, as each will be a closed type over - // a different combination of TMessage and TToken generic type parameters. Each existing recipient is also stored in - // the main recipients map, along with a set of all the existing dictionaries of handlers for that recipient (for all - // message types and token types). A recipient is stored in the main map as long as it has at least one - // registered handler in any of the existing mappings for every message/token type combination. - // The shared map is used to access the set of all registered handlers for a given recipient, without having - // to know in advance the type of message or token being used for the registration, and without having to - // use reflection. This is the same approach used in the types map, as we expose saved items as IMapping values too. - // Note that each mapping stored in the associated set for each recipient also indirectly implements - // IDictionarySlim, with any token type currently in use by that recipient. This allows to retrieve - // the type-closed mappings of registered handlers with a given token type, for any message type, for every receiver, - // again without having to use reflection. This shared map is used to unregister messages from a given recipients - // either unconditionally, by message type, by token, or for a specific pair of message type and token value. - - /// - /// The collection of currently registered recipients, with a link to their linked message receivers. - /// - /// - /// This collection is used to allow reflection-free access to all the existing - /// registered recipients from and other methods in this type, - /// so that all the existing handlers can be removed without having to dynamically create - /// the generic types for the containers of the various dictionaries mapping the handlers. - /// - private readonly DictionarySlim> recipientsMap = new(); - - /// - /// The instance for types combination. - /// - /// - /// The values are just of type as we don't know the type parameters in advance. - /// Each method relies on to get the type-safe instance - /// of the class for each pair of generic arguments in use. - /// - private readonly DictionarySlim typesMap = new(); - - /// - /// Gets the default instance. - /// - public static StrongReferenceMessenger Default { get; } = new(); - - /// - public bool IsRegistered(object recipient, TToken token) - where TMessage : class - where TToken : IEquatable - { - lock (this.recipientsMap) - { - if (!TryGetMapping(out Mapping? mapping)) - { - return false; - } - - Recipient key = new(recipient); - - return mapping.ContainsKey(key); - } - } - - /// - public void Register(TRecipient recipient, TToken token, MessageHandler handler) - where TRecipient : class - where TMessage : class - where TToken : IEquatable - { - lock (this.recipientsMap) - { - // Get the registration list for this recipient - Mapping mapping = GetOrAddMapping(); - Recipient key = new(recipient); - ref DictionarySlim? map = ref mapping.GetOrAddValueRef(key); - - map ??= new DictionarySlim(); - - // Add the new registration entry - ref object? registeredHandler = ref map.GetOrAddValueRef(token); - - if (registeredHandler is not null) - { - ThrowInvalidOperationExceptionForDuplicateRegistration(); - } - - // Treat the input delegate as if it was covariant (see comments below in the Send method) - registeredHandler = handler; - - // Make sure this registration map is tracked for the current recipient - ref HashSet? set = ref this.recipientsMap.GetOrAddValueRef(key); - - set ??= new HashSet(); - - _ = set.Add(mapping); - } - } - - /// - public void UnregisterAll(object recipient) - { - lock (this.recipientsMap) - { - // If the recipient has no registered messages at all, ignore - Recipient key = new(recipient); - - if (!this.recipientsMap.TryGetValue(key, out HashSet? set)) - { - return; - } - - // Removes all the lists of registered handlers for the recipient - foreach (IMapping mapping in set) - { - if (mapping.TryRemove(key) && - mapping.Count == 0) - { - // Maps here are really of type Mapping<,> and with unknown type arguments. - // If after removing the current recipient a given map becomes empty, it means - // that there are no registered recipients at all for a given pair of message - // and token types. In that case, we also remove the map from the types map. - // The reason for keeping a key in each mapping is that removing items from a - // dictionary (a hashed collection) only costs O(1) in the best case, while - // if we had tried to iterate the whole dictionary every time we would have - // paid an O(n) minimum cost for each single remove operation. - _ = this.typesMap.TryRemove(mapping.TypeArguments); - } - } - - // Remove the associated set in the recipients map - _ = this.recipientsMap.TryRemove(key); - } - } - - /// - public void UnregisterAll(object recipient, TToken token) - where TToken : IEquatable - { - bool lockTaken = false; - object[]? maps = null; - int i = 0; - - // We use an explicit try/finally block here instead of the lock syntax so that we can use a single - // one both to release the lock and to clear the rented buffer and return it to the pool. The reason - // why we're declaring the buffer here and clearing and returning it in this outer finally block is - // that doing so doesn't require the lock to be kept, and releasing it before performing this last - // step reduces the total time spent while the lock is acquired, which in turn reduces the lock - // contention in multi-threaded scenarios where this method is invoked concurrently. - try - { - Monitor.Enter(this.recipientsMap, ref lockTaken); - - // Get the shared set of mappings for the recipient, if present - Recipient key = new(recipient); - - if (!this.recipientsMap.TryGetValue(key, out HashSet? set)) - { - return; - } - - // Copy the candidate mappings for the target recipient to a local array, as we can't modify the - // contents of the set while iterating it. The rented buffer is oversized and will also include - // mappings for handlers of messages that are registered through a different token. Note that - // we're using just an object array to minimize the number of total rented buffers, that would - // just remain in the shared pool unused, other than when they are rented here. Instead, we're - // using a type that would possibly also be used by the users of the library, which increases - // the opportunities to reuse existing buffers for both. When we need to reference an item - // stored in the buffer with the type we know it will have, we use Unsafe.As to avoid the - // expensive type check in the cast, since we already know the assignment will be valid. - maps = ArrayPool.Shared.Rent(set.Count); - - foreach (IMapping item in set) - { - // Select all mappings using the same token type - if (item is IDictionarySlim> mapping) - { - maps[i++] = mapping; - } - } - - // Iterate through all the local maps. These are all the currently - // existing maps of handlers for messages of any given type, with a token - // of the current type, for the target recipient. We heavily rely on - // interfaces here to be able to iterate through all the available mappings - // without having to know the concrete type in advance, and without having - // to deal with reflection: we can just check if the type of the closed interface - // matches with the token type currently in use, and operate on those instances. - foreach (object obj in maps.AsSpan(0, i)) - { - var map = Unsafe.As>>(obj); - - // We don't need whether or not the map contains the recipient, as the - // sequence of maps has already been copied from the set containing all - // the mappings for the target recipients: it is guaranteed to be here. - IDictionarySlim holder = map[key]; - - // Try to remove the registered handler for the input token, - // for the current message type (unknown from here). - if (holder.TryRemove(token) && - holder.Count == 0) - { - // If the map is empty, remove the recipient entirely from its container - _ = map.TryRemove(key); - - // If no handlers are left at all for the recipient, across all - // message types and token types, remove the set of mappings - // entirely for the current recipient, and lost the strong - // reference to it as well. This is the same situation that - // would've been achieved by just calling UnregisterAll(recipient). - if (map.Count == 0 && - set.Remove(Unsafe.As(map)) && - set.Count == 0) - { - _ = this.recipientsMap.TryRemove(key); - } - } - } - } - finally - { - // Release the lock, if we did acquire it - if (lockTaken) - { - Monitor.Exit(this.recipientsMap); - } - - // If we got to renting the array of maps, return it to the shared pool. - // Remove references to avoid leaks coming from the shared memory pool. - // We manually create a span and clear it as a small optimization, as - // arrays rented from the pool can be larger than the requested size. - if (maps is not null) - { - maps.AsSpan(0, i).Clear(); - - ArrayPool.Shared.Return(maps); - } - } - } - - /// - public void Unregister(object recipient, TToken token) - where TMessage : class - where TToken : IEquatable - { - lock (this.recipientsMap) - { - // Get the registration list, if available - if (!TryGetMapping(out Mapping? mapping)) - { - return; - } - - Recipient key = new(recipient); - - if (!mapping.TryGetValue(key, out DictionarySlim? dictionary)) - { - return; - } - - // Remove the target handler - if (dictionary.TryRemove(token) && - dictionary.Count == 0) - { - // If the map is empty, it means that the current recipient has no remaining - // registered handlers for the current combination, regardless, - // of the specific token value (ie. the channel used to receive messages of that type). - // We can remove the map entirely from this container, and remove the link to the map itself - // to the current mapping between existing registered recipients (or entire recipients too). - _ = mapping.TryRemove(key); - - HashSet set = this.recipientsMap[key]; - - if (set.Remove(mapping) && - set.Count == 0) - { - _ = this.recipientsMap.TryRemove(key); - } - } - } - } - - /// - public TMessage Send(TMessage message, TToken token) - where TMessage : class - where TToken : IEquatable - { - object[] rentedArray; - Span pairs; - int i = 0; - - lock (this.recipientsMap) - { - // Check whether there are any registered recipients - _ = TryGetMapping(out Mapping? mapping); - - // We need to make a local copy of the currently registered handlers, since users might - // try to unregister (or register) new handlers from inside one of the currently existing - // handlers. We can use memory pooling to reuse arrays, to minimize the average memory - // usage. In practice, we usually just need to pay the small overhead of copying the items. - // The current mapping contains all the currently registered recipients and handlers for - // the combination in use. In the worst case scenario, all recipients - // will have a registered handler with a token matching the input one, meaning that we could - // have at worst a number of pending handlers to invoke equal to the total number of recipient - // in the mapping. This relies on the fact that tokens are unique, and that there is only - // one handler associated with a given token. We can use this upper bound as the requested - // size for each array rented from the pool, which guarantees that we'll have enough space. - int totalHandlersCount = mapping?.Count ?? 0; - - if (totalHandlersCount == 0) - { - return message; - } - - // Rent the array and also assign it to a span, which will be used to access values. - // We're doing this to avoid the array covariance checks slowdown in the loops below. - pairs = rentedArray = ArrayPool.Shared.Rent(2 * totalHandlersCount); - - // Copy the handlers to the local collection. - // The array is oversized at this point, since it also includes - // handlers for different tokens. We can reuse the same variable - // to count the number of matching handlers to invoke later on. - // This will be the array slice with valid handler in the rented buffer. - var mappingEnumerator = mapping!.GetEnumerator(); - - // Explicit enumerator usage here as we're using a custom one - // that doesn't expose the single standard Current property. - while (mappingEnumerator.MoveNext()) - { - // Pick the target handler, if the token is a match for the recipient - if (mappingEnumerator.Value.TryGetValue(token, out object? handler)) - { - // This span access should always guaranteed to be valid due to the size of the - // array being set according to the current total number of registered handlers, - // which will always be greater or equal than the ones matching the previous test. - // We're still using a checked span accesses here though to make sure an out of - // bounds write can never happen even if an error was present in the logic above. - pairs[2 * i] = handler; - pairs[(2 * i) + 1] = mappingEnumerator.Key.Target; - i++; - } - } - } - - try - { - // Invoke all the necessary handlers on the local copy of entries - for (int j = 0; j < i; j++) - { - // Here we perform an unsafe cast to enable covariance for delegate types. - // We know that the input recipient will always respect the type constraints - // of each original input delegate, and doing so allows us to still invoke - // them all from here without worrying about specific generic type arguments. - Unsafe.As>(pairs[2 * j])(pairs[(2 * j) + 1], message); - } - } - finally - { - // As before, we also need to clear it first to avoid having potentially long - // lasting memory leaks due to leftover references being stored in the pool. - Array.Clear(rentedArray, 0, 2 * i); - - ArrayPool.Shared.Return(rentedArray); - } - - return message; - } - - /// - void IMessenger.Cleanup() - { - // The current implementation doesn't require any kind of cleanup operation, as - // all the internal data structures are already kept in sync whenever a recipient - // is added or removed. This method is implemented through an explicit interface - // implementation so that developers using this type directly will not see it in - // the API surface (as it wouldn't be useful anyway, since it's a no-op here). - } - - /// - public void Reset() - { - lock (this.recipientsMap) - { - this.recipientsMap.Clear(); - this.typesMap.Clear(); - } - } - - /// - /// Tries to get the instance of currently registered recipients - /// for the combination of types and . - /// - /// The type of message to send. - /// The type of token to identify what channel to use to send the message. - /// The resulting instance, if found. - /// Whether or not the required instance was found. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool TryGetMapping([NotNullWhen(true)] out Mapping? mapping) - where TMessage : class - where TToken : IEquatable - { - Type2 key = new(typeof(TMessage), typeof(TToken)); - - if (this.typesMap.TryGetValue(key, out IMapping? target)) - { - // This method and the ones above are the only ones handling values in the types map, - // and here we are sure that the object reference we have points to an instance of the - // right type. Using an unsafe cast skips two conditional branches and is faster. - mapping = Unsafe.As>(target); - - return true; - } - - mapping = null; - - return false; - } - - /// - /// Gets the instance of currently registered recipients - /// for the combination of types and . - /// - /// The type of message to send. - /// The type of token to identify what channel to use to send the message. - /// A instance with the requested type arguments. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Mapping GetOrAddMapping() - where TMessage : class - where TToken : IEquatable - { - Type2 key = new(typeof(TMessage), typeof(TToken)); - ref IMapping? target = ref this.typesMap.GetOrAddValueRef(key); - - target ??= new Mapping(); - - return Unsafe.As>(target); - } - - /// - /// A mapping type representing a link to recipients and their view of handlers per communication channel. - /// - /// The type of message to receive. - /// The type of token to use to pick the messages to receive. - /// - /// This type is defined for simplicity and as a workaround for the lack of support for using type aliases - /// over open generic types in C# (using type aliases can only be used for concrete, closed types). - /// - private sealed class Mapping : DictionarySlim>, IMapping - where TMessage : class - where TToken : IEquatable - { - /// - /// Initializes a new instance of the class. - /// - public Mapping() - { - TypeArguments = new Type2(typeof(TMessage), typeof(TToken)); - } - - /// - public Type2 TypeArguments { get; } - } - - /// - /// An interface for the type which allows to retrieve the type - /// arguments from a given generic instance without having any prior knowledge about those arguments. - /// - private interface IMapping : IDictionarySlim - { - /// - /// Gets the instance representing the current type arguments. - /// - Type2 TypeArguments { get; } - } - - /// - /// A simple type representing a recipient. - /// - /// - /// This type is used to enable fast indexing in each mapping dictionary, - /// since it acts as an external override for the and - /// methods for arbitrary objects, removing both - /// the virtual call and preventing instances overriding those methods in this context. - /// Using this type guarantees that all the equality operations are always only done - /// based on reference equality for each registered recipient, regardless of its type. - /// - private readonly struct Recipient : IEquatable - { - /// - /// The registered recipient. - /// - public readonly object Target; - - /// - /// Initializes a new instance of the struct. - /// - /// The target recipient instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Recipient(object target) - { - Target = target; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Recipient other) - { - return ReferenceEquals(Target, other.Target); - } - - /// - public override bool Equals(object? obj) - { - return obj is Recipient other && Equals(other); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode() - { - return RuntimeHelpers.GetHashCode(this.Target); - } - } - - /// - /// Throws an when trying to add a duplicate handler. - /// - private static void ThrowInvalidOperationExceptionForDuplicateRegistration() - { - throw new InvalidOperationException("The target recipient has already subscribed to the target message"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Messaging/WeakReferenceMessenger.cs b/Microsoft.Toolkit.Mvvm/Messaging/WeakReferenceMessenger.cs deleted file mode 100644 index 231dc9d1870..00000000000 --- a/Microsoft.Toolkit.Mvvm/Messaging/WeakReferenceMessenger.cs +++ /dev/null @@ -1,363 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using Microsoft.Collections.Extensions; -using Microsoft.Toolkit.Mvvm.Messaging.Internals; -#if NETSTANDARD2_0 -using RecipientsTable = Microsoft.Toolkit.Mvvm.Messaging.Internals.ConditionalWeakTable2; -#else -using RecipientsTable = System.Runtime.CompilerServices.ConditionalWeakTable; -#endif - -#pragma warning disable SA1204 - -namespace Microsoft.Toolkit.Mvvm.Messaging -{ - /// - /// A class providing a reference implementation for the interface. - /// - /// - /// - /// This implementation uses weak references to track the registered - /// recipients, so it is not necessary to manually unregister them when they're no longer needed. - /// - /// - /// The type will automatically perform internal trimming when - /// full GC collections are invoked, so calling manually is not necessary to - /// ensure that on average the internal data structures are as trimmed and compact as possible. - /// Note: this is not supported when running on .NET Framework, due to app domain unloading issues. - /// - /// - public sealed class WeakReferenceMessenger : IMessenger - { - // This messenger uses the following logic to link stored instances together: - // -------------------------------------------------------------------------------------------------------- - // DictionarySlim> mapping - // / / / - // ___(Type2.TToken)___/ / / - // /_________________(Type2.TMessage)______________________/ / - // / ___________________________/ - // / / - // DictionarySlim> recipientsMap; - // -------------------------------------------------------------------------------------------------------- - // Just like in the strong reference variant, each pair of message and token types is used as a key in the - // recipients map. In this case, the values in the dictionary are ConditionalWeakTable<,> instances, that - // link each registered recipient to a map of currently registered handlers, through a weak reference. - // The value in each conditional table is Dictionary>, using - // the same unsafe cast as before to allow the generic handler delegates to be invoked without knowing - // what type each recipient was registered with, and without the need to use reflection. - - /// - /// The map of currently registered recipients for all message types. - /// - private readonly DictionarySlim recipientsMap = new(); - - /// - /// Initializes a new instance of the class. - /// - public WeakReferenceMessenger() - { - // Proxy function for the GC callback. This needs to be static and to take the target instance as - // an input parameter in order to avoid rooting it from the Gen2GcCallback object invoking it. - static void Gen2GcCallbackProxy(object target) - { - ((WeakReferenceMessenger)target).CleanupWithNonBlockingLock(); - } - - // Register an automatic GC callback to trigger a non-blocking cleanup. This will ensure that the - // current messenger instance is trimmed and without leftover recipient maps that are no longer used. - // This is necessary (as in, some form of cleanup, either explicit or automatic like in this case) - // because the ConditionalWeakTable instances will just remove key-value pairs on their - // own as soon as a key (ie. a recipient) is collected, causing their own keys (ie. the Type2 instances - // mapping to each conditional table for a pair of message and token types) to potentially remain in the - // root mapping structure but without any remaining recipients actually registered there, which just - // adds unnecessary overhead when trying to enumerate recipients during broadcasting operations later on. - Gen2GcCallback.Register(Gen2GcCallbackProxy, this); - } - - /// - /// Gets the default instance. - /// - public static WeakReferenceMessenger Default { get; } = new(); - - /// - public bool IsRegistered(object recipient, TToken token) - where TMessage : class - where TToken : IEquatable - { - lock (this.recipientsMap) - { - Type2 type2 = new(typeof(TMessage), typeof(TToken)); - - // Get the conditional table associated with the target recipient, for the current pair - // of token and message types. If it exists, check if there is a matching token. - return - this.recipientsMap.TryGetValue(type2, out RecipientsTable? table) && - table.TryGetValue(recipient, out IDictionarySlim? mapping) && - Unsafe.As>(mapping).ContainsKey(token); - } - } - - /// - public void Register(TRecipient recipient, TToken token, MessageHandler handler) - where TRecipient : class - where TMessage : class - where TToken : IEquatable - { - lock (this.recipientsMap) - { - Type2 type2 = new(typeof(TMessage), typeof(TToken)); - - // Get the conditional table for the pair of type arguments, or create it if it doesn't exist - ref RecipientsTable? mapping = ref this.recipientsMap.GetOrAddValueRef(type2); - - mapping ??= new RecipientsTable(); - - // Get or create the handlers dictionary for the target recipient - var map = Unsafe.As>(mapping.GetValue(recipient, static _ => new DictionarySlim())); - - // Add the new registration entry - ref object? registeredHandler = ref map.GetOrAddValueRef(token); - - if (registeredHandler is not null) - { - ThrowInvalidOperationExceptionForDuplicateRegistration(); - } - - // Store the input handler - registeredHandler = handler; - } - } - - /// - public void UnregisterAll(object recipient) - { - lock (this.recipientsMap) - { - var enumerator = this.recipientsMap.GetEnumerator(); - - // Traverse all the existing conditional tables and remove all the ones - // with the target recipient as key. We don't perform a cleanup here, - // as that is responsibility of a separate method defined below. - while (enumerator.MoveNext()) - { - _ = enumerator.Value.Remove(recipient); - } - } - } - - /// - public void UnregisterAll(object recipient, TToken token) - where TToken : IEquatable - { - lock (this.recipientsMap) - { - var enumerator = this.recipientsMap.GetEnumerator(); - - // Same as above, with the difference being that this time we only go through - // the conditional tables having a matching token type as key, and that we - // only try to remove handlers with a matching token, if any. - while (enumerator.MoveNext()) - { - if (enumerator.Key.TToken == typeof(TToken) && - enumerator.Value.TryGetValue(recipient, out IDictionarySlim? mapping)) - { - _ = Unsafe.As>(mapping).TryRemove(token); - } - } - } - } - - /// - public void Unregister(object recipient, TToken token) - where TMessage : class - where TToken : IEquatable - { - lock (this.recipientsMap) - { - Type2 type2 = new(typeof(TMessage), typeof(TToken)); - - // Get the target mapping table for the combination of message and token types, - // and remove the handler with a matching token (the entire map), if present. - if (this.recipientsMap.TryGetValue(type2, out RecipientsTable? value) && - value.TryGetValue(recipient, out IDictionarySlim? mapping)) - { - _ = Unsafe.As>(mapping).TryRemove(token); - } - } - } - - /// - public TMessage Send(TMessage message, TToken token) - where TMessage : class - where TToken : IEquatable - { - ArrayPoolBufferWriter bufferWriter; - int i = 0; - - lock (this.recipientsMap) - { - Type2 type2 = new(typeof(TMessage), typeof(TToken)); - - // Try to get the target table - if (!this.recipientsMap.TryGetValue(type2, out RecipientsTable? table)) - { - return message; - } - - bufferWriter = ArrayPoolBufferWriter.Create(); - - // We need a local, temporary copy of all the pending recipients and handlers to - // invoke, to avoid issues with handlers unregistering from messages while we're - // holding the lock. To do this, we can just traverse the conditional table in use - // to enumerate all the existing recipients for the token and message types pair - // corresponding to the generic arguments for this invocation, and then track the - // handlers with a matching token, and their corresponding recipients. - foreach (KeyValuePair pair in table) - { - var map = Unsafe.As>(pair.Value); - - if (map.TryGetValue(token, out object? handler)) - { - bufferWriter.Add(handler); - bufferWriter.Add(pair.Key); - i++; - } - } - } - - try - { - ReadOnlySpan pairs = bufferWriter.Span; - - for (int j = 0; j < i; j++) - { - // Just like in the other messenger, here we need an unsafe cast to be able to - // invoke a generic delegate with a contravariant input argument, with a less - // derived reference, without reflection. This is guaranteed to work by how the - // messenger tracks registered recipients and their associated handlers, so the - // type conversion will always be valid (the recipients are the rigth instances). - Unsafe.As>(pairs[2 * j])(pairs[(2 * j) + 1], message); - } - } - finally - { - bufferWriter.Dispose(); - } - - return message; - } - - /// - public void Cleanup() - { - lock (this.recipientsMap) - { - CleanupWithoutLock(); - } - } - - /// - public void Reset() - { - lock (this.recipientsMap) - { - this.recipientsMap.Clear(); - } - } - - /// - /// Executes a cleanup without locking the current instance. This method has to be - /// invoked when a lock on has already been acquired. - /// - private void CleanupWithNonBlockingLock() - { - object lockObject = this.recipientsMap; - bool lockTaken = false; - - try - { - Monitor.TryEnter(lockObject, ref lockTaken); - - if (lockTaken) - { - CleanupWithoutLock(); - } - } - finally - { - if (lockTaken) - { - Monitor.Exit(lockObject); - } - } - } - - /// - /// Executes a cleanup without locking the current instance. This method has to be - /// invoked when a lock on has already been acquired. - /// - private void CleanupWithoutLock() - { - using ArrayPoolBufferWriter type2s = ArrayPoolBufferWriter.Create(); - using ArrayPoolBufferWriter emptyRecipients = ArrayPoolBufferWriter.Create(); - - var enumerator = this.recipientsMap.GetEnumerator(); - - // First, we go through all the currently registered pairs of token and message types. - // These represents all the combinations of generic arguments with at least one registered - // handler, with the exception of those with recipients that have already been collected. - while (enumerator.MoveNext()) - { - emptyRecipients.Reset(); - - bool hasAtLeastOneHandler = false; - - // Go through the currently alive recipients to look for those with no handlers left. We track - // the ones we find to remove them outside of the loop (can't modify during enumeration). - foreach (KeyValuePair pair in enumerator.Value) - { - if (pair.Value.Count == 0) - { - emptyRecipients.Add(pair.Key); - } - else - { - hasAtLeastOneHandler = true; - } - } - - // Remove the handler maps for recipients that are still alive but with no handlers - foreach (object recipient in emptyRecipients.Span) - { - _ = enumerator.Value.Remove(recipient); - } - - // Track the type combinations with no recipients or handlers left - if (!hasAtLeastOneHandler) - { - type2s.Add(enumerator.Key); - } - } - - // Remove all the mappings with no handlers left - foreach (Type2 key in type2s.Span) - { - _ = this.recipientsMap.TryRemove(key); - } - } - - /// - /// Throws an when trying to add a duplicate handler. - /// - private static void ThrowInvalidOperationExceptionForDuplicateRegistration() - { - throw new InvalidOperationException("The target recipient has already subscribed to the target message"); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit.Mvvm/Microsoft.Toolkit.Mvvm.csproj b/Microsoft.Toolkit.Mvvm/Microsoft.Toolkit.Mvvm.csproj deleted file mode 100644 index efa6bad4691..00000000000 --- a/Microsoft.Toolkit.Mvvm/Microsoft.Toolkit.Mvvm.csproj +++ /dev/null @@ -1,56 +0,0 @@ - - - - Enable - true - netstandard2.0;netstandard2.1;net5.0 - - - - Windows Community Toolkit - MVVM (.NET Standard) - - This package includes a .NET Standard MVVM library with helpers such as: - - ObservableObject: a base class for objects implementing the INotifyPropertyChanged interface. - - ObservableRecipient: a base class for observable objects with support for the IMessenger service. - - ObservableValidator: a base class for objects implementing the INotifyDataErrorInfo interface. - - RelayCommand: a simple delegate command implementing the ICommand interface. - - AsyncRelayCommand: a delegate command supporting asynchronous operations and cancellation. - - WeakReferenceMessenger: a messaging system to exchange messages through different loosely-coupled objects. - - StrongReferenceMessenger: a high-performance messaging system that trades weak references for speed. - - Ioc: a helper class to configure dependency injection service containers. - - MVVM;Toolkit;MVVMToolkit;INotifyPropertyChanged;Observable;IOC;DI;Dependency Injection;Object Messaging;Extensions;Helpers - $(TargetsForTfmSpecificContentInPackage);CopyAnalyzerProjectReferencesToPackage - - - - - - - - - - - - - - - - - - - - - - - - - analyzers\dotnet\cs - - - - - \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs index de1fd087f53..1386027af70 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Controls/SampleAppMarkdownRenderer.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Toolkit.Helpers; using Microsoft.Toolkit.Parsers.Markdown; using Microsoft.Toolkit.Parsers.Markdown.Blocks; using Microsoft.Toolkit.Parsers.Markdown.Inlines; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index 0a9bf08f523..2018763e733 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -105,6 +105,9 @@ 10.1901.28001 + + 7.1.2 + ", RegexOptions.Singleline); - - /// - /// Regular expression for removing scripts from HTML. - /// - private static readonly Regex RemoveHtmlScriptsRegex = new(@"(?s)|)", RegexOptions.Singleline | RegexOptions.IgnoreCase); - - /// - /// Regular expression for removing styles from HTML. - /// - private static readonly Regex RemoveHtmlStylesRegex = new(@"(?s)|)", RegexOptions.Singleline | RegexOptions.IgnoreCase); - - /// - /// Determines whether a string is a valid email address. - /// - /// The string to test. - /// true for a valid email address; otherwise, false. - public static bool IsEmail(this string str) => Regex.IsMatch(str, EmailRegex); - - /// - /// Determines whether a string is a valid decimal number. - /// - /// The string to test. - /// true for a valid decimal number; otherwise, false. - public static bool IsDecimal([NotNullWhen(true)] this string? str) - { - return decimal.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out _); - } - - /// - /// Determines whether a string is a valid integer. - /// - /// The string to test. - /// true for a valid integer; otherwise, false. - public static bool IsNumeric([NotNullWhen(true)] this string? str) - { - return int.TryParse(str, out _); - } - - /// - /// Determines whether a string is a valid phone number. - /// - /// The string to test. - /// true for a valid phone number; otherwise, false. - public static bool IsPhoneNumber(this string str) => Regex.IsMatch(str, PhoneNumberRegex); - - /// - /// Determines whether a string contains only letters. - /// - /// The string to test. - /// true if the string contains only letters; otherwise, false. - public static bool IsCharacterString(this string str) => Regex.IsMatch(str, CharactersRegex); - - /// - /// Returns a string with HTML comments, scripts, styles, and tags removed. - /// - /// HTML string. - /// Decoded HTML string. - [return: NotNullIfNotNull("htmlText")] - public static string? DecodeHtml(this string? htmlText) - { - if (htmlText is null) - { - return null; - } - - var ret = htmlText.FixHtml(); - - // Remove html tags - ret = new Regex(RemoveHtmlTagsRegex).Replace(ret, string.Empty); - - return WebUtility.HtmlDecode(ret); - } - - /// - /// Returns a string with HTML comments, scripts, and styles removed. - /// - /// HTML string to fix. - /// Fixed HTML string. - public static string FixHtml(this string html) - { - // Remove comments - var withoutComments = RemoveHtmlCommentsRegex.Replace(html, string.Empty); - - // Remove scripts - var withoutScripts = RemoveHtmlScriptsRegex.Replace(withoutComments, string.Empty); - - // Remove styles - var withoutStyles = RemoveHtmlStylesRegex.Replace(withoutScripts, string.Empty); - - return withoutStyles; - } - - /// - /// Truncates a string to the specified length. - /// - /// The string to be truncated. - /// The maximum length. - /// Truncated string. - public static string Truncate(this string? value, int length) => Truncate(value, length, false); - - /// - /// Provide better linking for resourced strings. - /// - /// The format of the string being linked. - /// The object which will receive the linked String. - /// Truncated string. - [Obsolete("This method will be removed in a future version of the Toolkit. Use the native C# string interpolation syntax instead, see: https://docs.microsoft.com/dotnet/csharp/language-reference/tokens/interpolated")] - public static string AsFormat(this string format, params object[] args) - { - // Note: this extension was originally added to help developers using {x:Bind} in XAML, but - // due to a known limitation in the UWP/WinUI XAML compiler, using either this method or the - // standard string.Format method from the BCL directly doesn't always work. Since this method - // doesn't actually provide any benefit over the built-in one, it has been marked as obsolete. - // For more details, see the WinUI issue on the XAML compiler limitation here: - // https://github.com/microsoft/microsoft-ui-xaml/issues/2654. - return string.Format(format, args); - } - - /// - /// Truncates a string to the specified length. - /// - /// The string to be truncated. - /// The maximum length. - /// true to add ellipsis to the truncated text; otherwise, false. - /// Truncated string. - public static string Truncate(this string? value, int length, bool ellipsis) - { - if (!string.IsNullOrEmpty(value)) - { - value = value!.Trim(); - - if (value.Length > length) - { - if (ellipsis) - { - return value.Substring(0, length) + "..."; - } - - return value.Substring(0, length); - } - } - - return value ?? string.Empty; - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit/Extensions/TaskExtensions.cs b/Microsoft.Toolkit/Extensions/TaskExtensions.cs deleted file mode 100644 index e0cfb87fd20..00000000000 --- a/Microsoft.Toolkit/Extensions/TaskExtensions.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Contracts; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit -{ - /// - /// Helpers for working with tasks. - /// - public static class TaskExtensions - { - /// - /// Gets the result of a if available, or otherwise. - /// - /// The input instance to get the result for. - /// The result of if completed successfully, or otherwise. - /// - /// This method does not block if has not completed yet. Furthermore, it is not generic - /// and uses reflection to access the property and boxes the result if it's - /// a value type, which adds overhead. It should only be used when using generics is not possible. - /// - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object? GetResultOrDefault(this Task task) - { - // Check if the instance is a completed Task - if ( -#if NETSTANDARD2_1 - task.IsCompletedSuccessfully -#else - task.Status == TaskStatus.RanToCompletion -#endif - ) - { - // We need an explicit check to ensure the input task is not the cached - // Task.CompletedTask instance, because that can internally be stored as - // a Task for some given T (eg. on .NET 5 it's VoidTaskResult), which - // would cause the following code to return that result instead of null. - if (task != Task.CompletedTask) - { - // Try to get the Task.Result property. This method would've - // been called anyway after the type checks, but using that to - // validate the input type saves some additional reflection calls. - // Furthermore, doing this also makes the method flexible enough to - // cases whether the input Task is actually an instance of some - // runtime-specific type that inherits from Task. - PropertyInfo? propertyInfo = -#if NETSTANDARD1_4 - task.GetType().GetRuntimeProperty(nameof(Task.Result)); -#else - task.GetType().GetProperty(nameof(Task.Result)); -#endif - - // Return the result, if possible - return propertyInfo?.GetValue(task); - } - } - - return null; - } - - /// - /// Gets the result of a if available, or otherwise. - /// - /// The type of to get the result for. - /// The input instance to get the result for. - /// The result of if completed successfully, or otherwise. - /// This method does not block if has not completed yet. - [Pure] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? GetResultOrDefault(this Task task) - { -#if NETSTANDARD2_1 - return task.IsCompletedSuccessfully ? task.Result : default; -#else - return task.Status == TaskStatus.RanToCompletion ? task.Result : default; -#endif - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/DirectoryItemType.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/DirectoryItemType.cs deleted file mode 100644 index 150fa3770ac..00000000000 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/DirectoryItemType.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.Helpers -{ - /// - /// Represents the types of items available in a directory. - /// - public enum DirectoryItemType - { - /// - /// The item is neither a file or a folder. - /// - None, - - /// - /// Represents a file type item. - /// - File, - - /// - /// Represents a folder type item. - /// - Folder - } -} diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs deleted file mode 100644 index 7e45d82d7c9..00000000000 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IFileStorageHelper.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Helpers -{ - /// - /// Service interface used to store data in a directory/file-system via files and folders. - /// - /// This interface is meant to help abstract file storage operations across platforms in a library, - /// but the actual behavior will be up to the implementer. Such as, we don't provide a sense of a current directory, - /// so an implementor should consider using full paths to support any file operations. Otherwise, a "directory aware" - /// implementation could be achieved with a current directory field and traversal functions, in which case relative paths would be applicable. - /// - public interface IFileStorageHelper - { - /// - /// Retrieves an object from a file. - /// - /// Type of object retrieved. - /// Path to the file that contains the object. - /// Default value of the object. - /// Waiting task until completion with the object in the file. - Task ReadFileAsync(string filePath, T? @default = default); - - /// - /// Retrieves the listings for a folder and the item types. - /// - /// The path to the target folder. - /// A list of item types and names in the target folder. - Task> ReadFolderAsync(string folderPath); - - /// - /// Saves an object inside a file. - /// - /// Type of object saved. - /// Path to the file that will contain the object. - /// Object to save. - /// Waiting task until completion. - Task CreateFileAsync(string filePath, T value); - - /// - /// Ensure a folder exists at the folder path specified. - /// - /// The path and name of the target folder. - /// Waiting task until completion. - Task CreateFolderAsync(string folderPath); - - /// - /// Deletes a file or folder item. - /// - /// The path to the item for deletion. - /// Waiting task until completion. - Task TryDeleteItemAsync(string itemPath); - - /// - /// Rename an item. - /// - /// The path to the target item. - /// The new nam for the target item. - /// Waiting task until completion. - Task TryRenameItemAsync(string itemPath, string newName); - } -} diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs deleted file mode 100644 index 5a8177b4f78..00000000000 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/IObjectSerializer.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Toolkit.Helpers -{ - /// - /// A basic serialization service. - /// - public interface IObjectSerializer - { - /// - /// Serialize an object into a string. It is recommended to use strings as the final format for objects. - /// - /// The type of the object to serialize. - /// The object to serialize. - /// The serialized object. - string? Serialize(T value); - - /// - /// Deserialize string into an object of the given type. - /// - /// The type of the deserialized object. - /// The string to deserialize. - /// The deserialized object. - T Deserialize(string value); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs deleted file mode 100644 index c8f9713b5d8..00000000000 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/ISettingsStorageHelper.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; - -namespace Microsoft.Toolkit.Helpers -{ - /// - /// Service interface used to store data using key value pairs. - /// - /// The type of keys to use for accessing values. - public interface ISettingsStorageHelper - where TKey : notnull - { - /// - /// Retrieves a single item by its key. - /// - /// Type of object retrieved. - /// Key of the object. - /// The object for key. - /// A boolean indicator of success. - bool TryRead(TKey key, out TValue? value); - - /// - /// Saves a single item by its key. - /// - /// Type of object saved. - /// Key of the value saved. - /// Object to save. - void Save(TKey key, TValue value); - - /// - /// Deletes a single item by its key. - /// - /// Key of the object. - /// A boolean indicator of success. - bool TryDelete(TKey key); - - /// - /// Clear all keys and values from the settings store. - /// - void Clear(); - } -} diff --git a/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs b/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs deleted file mode 100644 index 62d1b681c06..00000000000 --- a/Microsoft.Toolkit/Helpers/ObjectStorage/SystemSerializer.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; - -namespace Microsoft.Toolkit.Helpers -{ - /// - /// A bare-bones serializer which knows how to deal with primitive types and strings only. - /// It is recommended for more complex scenarios to implement your own based on System.Text.Json, Newtonsoft.Json, or DataContractJsonSerializer see https://aka.ms/wct/storagehelper-migration - /// - public class SystemSerializer : IObjectSerializer - { - /// - /// Take a primitive value from storage and return it as the requested type using the API. - /// - /// Type to convert value to. - /// Value from storage to convert. - /// Deserialized value or default value. - public T Deserialize(string value) - { - var type = typeof(T); - var typeInfo = type.GetTypeInfo(); - - if (typeInfo.IsPrimitive || type == typeof(string)) - { - return (T)Convert.ChangeType(value, type); - } - - throw new NotSupportedException("This serializer can only handle primitive types and strings. Please implement your own IObjectSerializer for more complex scenarios."); - } - - /// - /// Returns the value so that it can be serialized directly. - /// - /// Type to serialize from. - /// Value to serialize. - /// String representation of value. - public string? Serialize(T value) - { - return value?.ToString(); - } - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit/IncrementalLoadingCollection/IIncrementalSource.cs b/Microsoft.Toolkit/IncrementalLoadingCollection/IIncrementalSource.cs deleted file mode 100644 index 8e46e20c9ba..00000000000 --- a/Microsoft.Toolkit/IncrementalLoadingCollection/IIncrementalSource.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Toolkit.Collections -{ - /// - /// This interface represents a data source whose items can be loaded incrementally. - /// - /// Type of collection element. - public interface IIncrementalSource - { - /// - /// This method is invoked every time the view need to show more items. Retrieves items based on and arguments. - /// - /// - /// The zero-based index of the page that corresponds to the items to retrieve. - /// - /// - /// The number of items to retrieve for the specified . - /// - /// - /// Used to propagate notification that operation should be canceled. - /// - /// - /// Returns a collection of . - /// - Task> GetPagedItemsAsync(int pageIndex, int pageSize, CancellationToken cancellationToken = default(CancellationToken)); - } -} \ No newline at end of file diff --git a/Microsoft.Toolkit/Microsoft.Toolkit.csproj b/Microsoft.Toolkit/Microsoft.Toolkit.csproj deleted file mode 100644 index 6efb4c97210..00000000000 --- a/Microsoft.Toolkit/Microsoft.Toolkit.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - Enable - true - netstandard1.4;netstandard2.0;netstandard2.1;net5.0 - - - - Windows Community Toolkit - Common (.NET Standard) - - This package includes .NET Standard code only helpers such as: - - IncrementalLoadingCollection: Simplifies the definition and usage of collections whose items can be loaded incrementally only when needed by the view. - - String extensions and array extensions: These extensions make working with string and arrays easier. - - Incremental;Loading;Collection;IncrementalLoadingCollection;String;Array;Extensions;Helpers - - - - - NETSTANDARD2_1_OR_GREATER - - - - - - - - - \ No newline at end of file diff --git a/SmokeTests/Microsoft.Toolkit.Diagnostics/MainPage.xaml b/SmokeTests/Microsoft.Toolkit.Diagnostics/MainPage.xaml deleted file mode 100644 index 7f4b7ee447c..00000000000 --- a/SmokeTests/Microsoft.Toolkit.Diagnostics/MainPage.xaml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - -