From 9cdc8ccb4f5d25dc12913ec952ad48f2c0833788 Mon Sep 17 00:00:00 2001 From: bbradson <76005458+bbradson@users.noreply.github.com> Date: Mon, 24 Oct 2022 18:56:18 +0200 Subject: [PATCH] Fishery --- 1.3/Assemblies/1Fishery.dll | Bin 0 -> 200192 bytes 1.3/Assemblies/IndexRange.dll | Bin 0 -> 11776 bytes ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 18024 bytes 1.4/Assemblies/1Fishery.dll | Bin 0 -> 200192 bytes 1.4/Assemblies/IndexRange.dll | Bin 0 -> 11776 bytes ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 18024 bytes About/About.xml | 28 + LICENSE | 8 +- README.md | 3 +- Source/FisheryLib.Tests/.editorconfig | 185 + .../FisheryLib.Tests/FisheryLib.Tests.csproj | 65 + Source/FisheryLib.Tests/FisheryLib.Tests.sln | 25 + Source/FisheryLib.Tests/UnitTest1.cs | 13 + Source/FisheryLib/.editorconfig | 185 + Source/FisheryLib/Aliases.cs | 16 + Source/FisheryLib/ArraySortHelper.cs | 1163 ++++++ Source/FisheryLib/BoolExtensions.cs | 77 + Source/FisheryLib/CollectionExtensions.cs | 333 ++ Source/FisheryLib/Convert.cs | 19 + .../CallerArgumentExpressionAttribute.cs | 24 + .../Diagnostics/Extensions/TypeExtensions.cs | 162 + .../Extensions/ValueTypeExtensions.cs | 66 + .../Generated/Guard.Collection.g.cs | 1822 +++++++++ .../Diagnostics/Generated/Guard.Collection.tt | 296 ++ .../Generated/Guard.Comparable.Numeric.g.cs | 3528 +++++++++++++++++ .../Generated/Guard.Comparable.Numeric.tt | 270 ++ .../FisheryLib/Diagnostics/Generated/Guard.md | 17 + .../Generated/ThrowHelper.Collection.g.cs | 810 ++++ .../Generated/ThrowHelper.Collection.tt | 121 + .../Diagnostics/Generated/TypeInfo.ttinclude | 116 + .../FisheryLib/Diagnostics/Guard.Boolean.cs | 356 ++ .../Diagnostics/Guard.Comparable.Generic.cs | 439 ++ .../Diagnostics/Guard.Comparable.Numeric.cs | 213 + Source/FisheryLib/Diagnostics/Guard.IO.cs | 80 + Source/FisheryLib/Diagnostics/Guard.String.cs | 334 ++ Source/FisheryLib/Diagnostics/Guard.Tasks.cs | 184 + Source/FisheryLib/Diagnostics/Guard.cs | 248 ++ .../Guard.Collection.Generic.ThrowHelper.cs | 47 + .../Guard.Comparable.Generic.ThrowHelper.cs | 123 + .../Guard.Comparable.Numeric.ThrowHelper.cs | 75 + .../Internals/Guard.IO.ThrowHelper.cs | 40 + .../Internals/Guard.String.ThrowHelper.cs | 158 + .../Internals/Guard.Tasks.ThrowHelper.cs | 76 + .../Internals/Guard.ThrowHelper.cs | 144 + .../Diagnostics/ThrowHelper.Generic.cs | 843 ++++ Source/FisheryLib/Diagnostics/ThrowHelper.cs | 683 ++++ Source/FisheryLib/Equality.cs | 29 + Source/FisheryLib/FishPatch.cs | 338 ++ Source/FisheryLib/FishTranspiler.cs | 2403 +++++++++++ Source/FisheryLib/Fishery.cs | 61 + Source/FisheryLib/Fishery.csproj | 66 + Source/FisheryLib/Fishery.sln | 25 + .../FisheryLib/FisheryPatchClassProcessor.cs | 210 + Source/FisheryLib/FunctionPointers/Convert.cs | 86 + Source/FisheryLib/FunctionPointers/Equals.cs | 122 + .../FunctionPointers/EqualsByRef.cs | 122 + .../FunctionPointers/GetHashCode.cs | 106 + .../FunctionPointers/GetHashCodeByRef.cs | 98 + Source/FisheryLib/HarmonyPatchExtensions.cs | 30 + Source/FisheryLib/HashCode.cs | 55 + Source/FisheryLib/InfixMethod.cs | 338 ++ Source/FisheryLib/Log.cs | 24 + Source/FisheryLib/MemoryMarshal.cs | 563 +++ Source/FisheryLib/ReadOnlySpanEnumerable.cs | 103 + Source/FisheryLib/ReadOnlySpanExtensions.cs | 574 +++ Source/FisheryLib/Reflection.cs | 240 ++ Source/FisheryLib/SingletonFactory.cs | 44 + Source/FisheryLib/SpanEnumerable.cs | 107 + Source/FisheryLib/SpanExtensions.cs | 552 +++ Source/FisheryLib/Utility.cs | 67 + 70 files changed, 19754 insertions(+), 4 deletions(-) create mode 100644 1.3/Assemblies/1Fishery.dll create mode 100644 1.3/Assemblies/IndexRange.dll create mode 100644 1.3/Assemblies/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 1.4/Assemblies/1Fishery.dll create mode 100644 1.4/Assemblies/IndexRange.dll create mode 100644 1.4/Assemblies/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 About/About.xml create mode 100644 Source/FisheryLib.Tests/.editorconfig create mode 100644 Source/FisheryLib.Tests/FisheryLib.Tests.csproj create mode 100644 Source/FisheryLib.Tests/FisheryLib.Tests.sln create mode 100644 Source/FisheryLib.Tests/UnitTest1.cs create mode 100644 Source/FisheryLib/.editorconfig create mode 100644 Source/FisheryLib/Aliases.cs create mode 100644 Source/FisheryLib/ArraySortHelper.cs create mode 100644 Source/FisheryLib/BoolExtensions.cs create mode 100644 Source/FisheryLib/CollectionExtensions.cs create mode 100644 Source/FisheryLib/Convert.cs create mode 100644 Source/FisheryLib/Diagnostics/Attributes/CallerArgumentExpressionAttribute.cs create mode 100644 Source/FisheryLib/Diagnostics/Extensions/TypeExtensions.cs create mode 100644 Source/FisheryLib/Diagnostics/Extensions/ValueTypeExtensions.cs create mode 100644 Source/FisheryLib/Diagnostics/Generated/Guard.Collection.g.cs create mode 100644 Source/FisheryLib/Diagnostics/Generated/Guard.Collection.tt create mode 100644 Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.g.cs create mode 100644 Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.tt create mode 100644 Source/FisheryLib/Diagnostics/Generated/Guard.md create mode 100644 Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.g.cs create mode 100644 Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.tt create mode 100644 Source/FisheryLib/Diagnostics/Generated/TypeInfo.ttinclude create mode 100644 Source/FisheryLib/Diagnostics/Guard.Boolean.cs create mode 100644 Source/FisheryLib/Diagnostics/Guard.Comparable.Generic.cs create mode 100644 Source/FisheryLib/Diagnostics/Guard.Comparable.Numeric.cs create mode 100644 Source/FisheryLib/Diagnostics/Guard.IO.cs create mode 100644 Source/FisheryLib/Diagnostics/Guard.String.cs create mode 100644 Source/FisheryLib/Diagnostics/Guard.Tasks.cs create mode 100644 Source/FisheryLib/Diagnostics/Guard.cs create mode 100644 Source/FisheryLib/Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs create mode 100644 Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs create mode 100644 Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs create mode 100644 Source/FisheryLib/Diagnostics/Internals/Guard.IO.ThrowHelper.cs create mode 100644 Source/FisheryLib/Diagnostics/Internals/Guard.String.ThrowHelper.cs create mode 100644 Source/FisheryLib/Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs create mode 100644 Source/FisheryLib/Diagnostics/Internals/Guard.ThrowHelper.cs create mode 100644 Source/FisheryLib/Diagnostics/ThrowHelper.Generic.cs create mode 100644 Source/FisheryLib/Diagnostics/ThrowHelper.cs create mode 100644 Source/FisheryLib/Equality.cs create mode 100644 Source/FisheryLib/FishPatch.cs create mode 100644 Source/FisheryLib/FishTranspiler.cs create mode 100644 Source/FisheryLib/Fishery.cs create mode 100644 Source/FisheryLib/Fishery.csproj create mode 100644 Source/FisheryLib/Fishery.sln create mode 100644 Source/FisheryLib/FisheryPatchClassProcessor.cs create mode 100644 Source/FisheryLib/FunctionPointers/Convert.cs create mode 100644 Source/FisheryLib/FunctionPointers/Equals.cs create mode 100644 Source/FisheryLib/FunctionPointers/EqualsByRef.cs create mode 100644 Source/FisheryLib/FunctionPointers/GetHashCode.cs create mode 100644 Source/FisheryLib/FunctionPointers/GetHashCodeByRef.cs create mode 100644 Source/FisheryLib/HarmonyPatchExtensions.cs create mode 100644 Source/FisheryLib/HashCode.cs create mode 100644 Source/FisheryLib/InfixMethod.cs create mode 100644 Source/FisheryLib/Log.cs create mode 100644 Source/FisheryLib/MemoryMarshal.cs create mode 100644 Source/FisheryLib/ReadOnlySpanEnumerable.cs create mode 100644 Source/FisheryLib/ReadOnlySpanExtensions.cs create mode 100644 Source/FisheryLib/Reflection.cs create mode 100644 Source/FisheryLib/SingletonFactory.cs create mode 100644 Source/FisheryLib/SpanEnumerable.cs create mode 100644 Source/FisheryLib/SpanExtensions.cs create mode 100644 Source/FisheryLib/Utility.cs diff --git a/1.3/Assemblies/1Fishery.dll b/1.3/Assemblies/1Fishery.dll new file mode 100644 index 0000000000000000000000000000000000000000..a26c4b707b292016a3845c095a837bb3bedffb25 GIT binary patch literal 200192 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 z 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

LSoC^v%|XX8Or8+ly4fCN}026A6** zoOCq%w71NrBF$z;vmcODb{MIIU2gogWMwVk)tX5qY*eF@lATTA#AdTnh^)z$A(gN_ z*~L;V))U@~GFHMm)!!}oF)yc}Ll7CjdJ_$4^s^MmZV+t%Unm=G@JKN);cOyON$~!Z z%AjyI14-lwKDayJxm<4CSFX#J5RGcoSFXqQbAr+LWVe%}*xYDAo$61Qo$N%6phMZu z$_7i06*L64$i*s&Hh?dd&4>dZjPMn#5{+Z)8VI@%D4w0<57?$W8?y6Ae0H}e4cTQR zKD(TH!&d-ecGX%9*-E4mme{PjlEnHp0vFEbL?wlt<%IKjOK!%z;1!b7p6mun3s#B5 z=kqZoi>*xNz7lpQ`<9%|;1&>RNTbFy9 zfCjw`X*5yEXL`Dz4d5$adpXg3KEf*b$7Mi#J_}iRYaS``e3UtnX0y2L-+~@x9bpq0 zo5^-IwgnGjeFf>IwY-BLV~-FmZ_G*j+il@nRWG<_X{$FQkH{oC{k9>+GA^hoft?665s22W&OZFrw0Y(UV2;K{5AX*PSum>K*W zJ7Lm<;1^f`e6Geve=Fei;Fs7`qVn3W2hU_W zeI;yQ{CB~xu~8=3f?sFTIl(N ai3# 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 literal 0 HcmV?d00001 diff --git a/1.3/Assemblies/IndexRange.dll b/1.3/Assemblies/IndexRange.dll new file mode 100644 index 0000000000000000000000000000000000000000..be06f22358f3e19b19ac7aa206a57c6fc104ff47 GIT binary patch literal 11776 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´RVDkZYh5I? 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 literal 0 HcmV?d00001 diff --git a/1.4/Assemblies/1Fishery.dll b/1.4/Assemblies/1Fishery.dll new file mode 100644 index 0000000000000000000000000000000000000000..46ac14de6fdfd09939405786f8463d5909db6198 GIT binary patch literal 200192 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´RVDkZYh5I? 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 literal 0 HcmV?d00001 diff --git a/About/About.xml b/About/About.xml new file mode 100644 index 0000000..d71e95d --- /dev/null +++ b/About/About.xml @@ -0,0 +1,28 @@ + + + Fishery - Modding Library + bs.fishery + +

  • 1.3
  • +
  • 1.4
  • + + bradson + https://github.com/bbradson/fishery + +
  • Ludeon.RimWorld
  • +
  • me.samboycoding.betterloading
  • +
  • zetrith.prepatcher
  • +
    + +
  • brrainz.harmony
  • +
    + +
  • + brrainz.harmony + Harmony + steam://url/CommunityFilePage/2009463077 + https://github.com/pardeike/HarmonyRimWorld/releases/latest +
  • +
    + Additions for Harmony to simplify otherwise complex patch operations + \ No newline at end of file diff --git a/LICENSE b/LICENSE index a612ad9..a7064a9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ +Copyright (c) 2022 bradson + Mozilla Public License Version 2.0 ================================== @@ -35,7 +37,7 @@ Mozilla Public License Version 2.0 means any form of the work other than Source Code Form. 1.7. "Larger Work" - means a work that combines Covered Software with other material, in + means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" @@ -357,7 +359,7 @@ Exhibit A - Source Code Form License Notice This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. + file, You can obtain one at https://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE @@ -370,4 +372,4 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file diff --git a/README.md b/README.md index 568efab..c067c6f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # Fishery - Modding Library +Modding Library for RimWorld. Used by Performance Fish. +Useful functions include various abstractions around IL in FishTranspiler, to make writing of transpilers easier and more robust. A couple collections like Dictionary and Span have extension methods for fast and convenient lookups. Some functionalities of Microsoft's Community Toolkit are included, expanded upon and modified to better fit Unity. Equality includes generic extension methods to use in place of Equals on structs without memory allocations. Convert provides a faster, generic alternative to System.Convert.ToType. FisheryLib.Reflection provides a couple reflection helpers, Aliases has a methodof function intended to be utilized with `global using static Aliases` to directly retrieve method infos from static method groups. diff --git a/Source/FisheryLib.Tests/.editorconfig b/Source/FisheryLib.Tests/.editorconfig new file mode 100644 index 0000000..6cde0c3 --- /dev/null +++ b/Source/FisheryLib.Tests/.editorconfig @@ -0,0 +1,185 @@ +[*.cs] + +file_header_template = /* Copyright (c) 2022 bradson\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */ + +# CA2211: Non-constant fields should not be visible +dotnet_diagnostic.CA2211.severity = none +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = when_multiline:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:silent +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +[*.cs] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.constant_should_be_all_upper.severity = suggestion +dotnet_naming_rule.constant_should_be_all_upper.symbols = constant +dotnet_naming_rule.constant_should_be_all_upper.style = all_upper + +dotnet_naming_rule.private_or_protected_field_should_be_camel_case_with__.severity = suggestion +dotnet_naming_rule.private_or_protected_field_should_be_camel_case_with__.symbols = private_or_protected_field +dotnet_naming_rule.private_or_protected_field_should_be_camel_case_with__.style = camel_case_with__ + +dotnet_naming_rule.public_or_internal_field_should_be_camel_case.severity = silent +dotnet_naming_rule.public_or_internal_field_should_be_camel_case.symbols = public_or_internal_field +dotnet_naming_rule.public_or_internal_field_should_be_camel_case.style = camel_case + +# Symbol specifications + +dotnet_naming_symbols.constant.applicable_kinds = field, local +dotnet_naming_symbols.constant.applicable_accessibilities = * +dotnet_naming_symbols.constant.required_modifiers = const + +dotnet_naming_symbols.private_or_protected_field.applicable_kinds = field +dotnet_naming_symbols.private_or_protected_field.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_or_protected_field.required_modifiers = + +dotnet_naming_symbols.public_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.public_or_internal_field.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_or_internal_field.required_modifiers = + +# Naming styles + +dotnet_naming_style.all_upper.required_prefix = +dotnet_naming_style.all_upper.required_suffix = +dotnet_naming_style.all_upper.word_separator = _ +dotnet_naming_style.all_upper.capitalization = all_upper + +dotnet_naming_style.camel_case_with__.required_prefix = _ +dotnet_naming_style.camel_case_with__.required_suffix = +dotnet_naming_style.camel_case_with__.word_separator = +dotnet_naming_style.camel_case_with__.capitalization = camel_case + +dotnet_naming_style.camel_case.required_prefix = +dotnet_naming_style.camel_case.required_suffix = +dotnet_naming_style.camel_case.word_separator = +dotnet_naming_style.camel_case.capitalization = camel_case +dotnet_diagnostic.CA1805.severity = none +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false:suggestion +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_space_around_binary_operators = before_and_after + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_diagnostic.CA1838.severity = suggestion +dotnet_diagnostic.CA1848.severity = suggestion +dotnet_diagnostic.CA2215.severity = suggestion +dotnet_style_readonly_field = true:silent +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning +dotnet_style_allow_multiple_blank_lines_experimental = false:suggestion +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = non_public:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion +indent_style = tab diff --git a/Source/FisheryLib.Tests/FisheryLib.Tests.csproj b/Source/FisheryLib.Tests/FisheryLib.Tests.csproj new file mode 100644 index 0000000..0843495 --- /dev/null +++ b/Source/FisheryLib.Tests/FisheryLib.Tests.csproj @@ -0,0 +1,65 @@ + + + + net48 + preview + True + preview-all + True + v1.3;v1.4 + + + + + + + E:\RimWorld\1.3\RimWorldWin64_Data\Managed\Assembly-CSharp.dll + + + E:\RimWorld\1.3\RimWorldWin64_Data\Managed\mscorlib.dll + + + E:\RimWorld\1.3\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll + + + E:\RimWorld\1.3\RimWorldWin64_Data\Managed\UnityEngine.IMGUIModule.dll + + + ..\..\1.3\Assemblies\1Fishery.dll + + + + + + + + E:\RimWorld\1.4\RimWorldWin64_Data\Managed\Assembly-CSharp.dll + + + E:\RimWorld\1.4\RimWorldWin64_Data\Managed\mscorlib.dll + + + E:\RimWorld\1.4\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll + + + E:\RimWorld\1.4\RimWorldWin64_Data\Managed\UnityEngine.IMGUIModule.dll + + + ..\..\1.4\Assemblies\1Fishery.dll + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + \ No newline at end of file diff --git a/Source/FisheryLib.Tests/FisheryLib.Tests.sln b/Source/FisheryLib.Tests/FisheryLib.Tests.sln new file mode 100644 index 0000000..58c484d --- /dev/null +++ b/Source/FisheryLib.Tests/FisheryLib.Tests.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32728.150 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FisheryLib.Tests", "FisheryLib.Tests.csproj", "{5BFAFA41-9B21-4EC3-B3EB-5CBF6D0A300C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + v1.3|Any CPU = v1.3|Any CPU + v1.4|Any CPU = v1.4|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5BFAFA41-9B21-4EC3-B3EB-5CBF6D0A300C}.v1.3|Any CPU.ActiveCfg = v1.3|Any CPU + {5BFAFA41-9B21-4EC3-B3EB-5CBF6D0A300C}.v1.3|Any CPU.Build.0 = v1.3|Any CPU + {5BFAFA41-9B21-4EC3-B3EB-5CBF6D0A300C}.v1.4|Any CPU.ActiveCfg = v1.4|Any CPU + {5BFAFA41-9B21-4EC3-B3EB-5CBF6D0A300C}.v1.4|Any CPU.Build.0 = v1.4|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {832FF2F6-0C42-42BD-8599-6288138CCD01} + EndGlobalSection +EndGlobal diff --git a/Source/FisheryLib.Tests/UnitTest1.cs b/Source/FisheryLib.Tests/UnitTest1.cs new file mode 100644 index 0000000..8189662 --- /dev/null +++ b/Source/FisheryLib.Tests/UnitTest1.cs @@ -0,0 +1,13 @@ +global using System; +global using Xunit; + +namespace FisheryLib.Tests; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } +} \ No newline at end of file diff --git a/Source/FisheryLib/.editorconfig b/Source/FisheryLib/.editorconfig new file mode 100644 index 0000000..14c2131 --- /dev/null +++ b/Source/FisheryLib/.editorconfig @@ -0,0 +1,185 @@ +[*.cs] + +file_header_template = Copyright (c) 2022 bradson\nThis Source Code Form is subject to the terms of the Mozilla Public\nLicense, v. 2.0. If a copy of the MPL was not distributed with this\nfile, You can obtain one at https://mozilla.org/MPL/2.0/. + +# CA2211: Non-constant fields should not be visible +dotnet_diagnostic.CA2211.severity = none +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = when_multiline:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:silent +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion + +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +[*.cs] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.constant_should_be_all_upper.severity = suggestion +dotnet_naming_rule.constant_should_be_all_upper.symbols = constant +dotnet_naming_rule.constant_should_be_all_upper.style = all_upper + +dotnet_naming_rule.private_or_protected_field_should_be_camel_case_with__.severity = suggestion +dotnet_naming_rule.private_or_protected_field_should_be_camel_case_with__.symbols = private_or_protected_field +dotnet_naming_rule.private_or_protected_field_should_be_camel_case_with__.style = camel_case_with__ + +dotnet_naming_rule.public_or_internal_field_should_be_camel_case.severity = silent +dotnet_naming_rule.public_or_internal_field_should_be_camel_case.symbols = public_or_internal_field +dotnet_naming_rule.public_or_internal_field_should_be_camel_case.style = camel_case + +# Symbol specifications + +dotnet_naming_symbols.constant.applicable_kinds = field, local +dotnet_naming_symbols.constant.applicable_accessibilities = * +dotnet_naming_symbols.constant.required_modifiers = const + +dotnet_naming_symbols.private_or_protected_field.applicable_kinds = field +dotnet_naming_symbols.private_or_protected_field.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_or_protected_field.required_modifiers = + +dotnet_naming_symbols.public_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.public_or_internal_field.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_or_internal_field.required_modifiers = + +# Naming styles + +dotnet_naming_style.all_upper.required_prefix = +dotnet_naming_style.all_upper.required_suffix = +dotnet_naming_style.all_upper.word_separator = _ +dotnet_naming_style.all_upper.capitalization = all_upper + +dotnet_naming_style.camel_case_with__.required_prefix = _ +dotnet_naming_style.camel_case_with__.required_suffix = +dotnet_naming_style.camel_case_with__.word_separator = +dotnet_naming_style.camel_case_with__.capitalization = camel_case + +dotnet_naming_style.camel_case.required_prefix = +dotnet_naming_style.camel_case.required_suffix = +dotnet_naming_style.camel_case.word_separator = +dotnet_naming_style.camel_case.capitalization = camel_case +dotnet_diagnostic.CA1805.severity = none +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_prefer_static_local_function = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false:suggestion +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_space_around_binary_operators = before_and_after + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_diagnostic.CA1838.severity = suggestion +dotnet_diagnostic.CA1848.severity = suggestion +dotnet_diagnostic.CA2215.severity = suggestion +dotnet_style_readonly_field = true:silent +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning +dotnet_style_allow_multiple_blank_lines_experimental = false:suggestion +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = non_public:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion +indent_style = tab diff --git a/Source/FisheryLib/Aliases.cs b/Source/FisheryLib/Aliases.cs new file mode 100644 index 0000000..fbaade2 --- /dev/null +++ b/Source/FisheryLib/Aliases.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2022 bradson +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +global using static FisheryLib.Aliases; +using System.Runtime.CompilerServices; + +namespace FisheryLib; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "alias")] +public static class Aliases +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MethodInfo methodof(Delegate method) => method.Method; +} \ No newline at end of file diff --git a/Source/FisheryLib/ArraySortHelper.cs b/Source/FisheryLib/ArraySortHelper.cs new file mode 100644 index 0000000..7e91c41 --- /dev/null +++ b/Source/FisheryLib/ArraySortHelper.cs @@ -0,0 +1,1163 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FisheryLib; +#region ArraySortHelper for single arrays + +internal sealed class ArraySortHelper : IArraySortHelper +{ + public static IArraySortHelper Default { get; } = CreateArraySortHelper(); + + private static IArraySortHelper CreateArraySortHelper() + => typeof(IComparable).IsAssignableFrom(typeof(T)) + ? (IArraySortHelper)Activator.CreateInstance(typeof(GenericArraySortHelper<>).MakeGenericType(typeof(T))) + : new ArraySortHelper(); + + #region IArraySortHelper Members + + public void Sort(Span keys, IComparer? comparer) + { + // Add a try block here to detect IComparers (or their + // underlying IComparables, etc) that are bogus. + try + { + comparer ??= Comparer.Default; + IntrospectiveSort(keys, comparer.Compare); + } + catch (IndexOutOfRangeException) + { + SortUtils.ThrowArgumentException_BadComparer(comparer); + } + catch (Exception e) + { + ThrowHelper.ThrowInvalidOperationException(SortUtils.InvalidOperation_IComparerFailed, e); + } + } + + #endregion + + public static void Sort(Span keys, Comparison comparer) + { + Debug.Assert(comparer != null, "Check the arguments in the caller!"); + + // Add a try block here to detect bogus comparisons + try + { + IntrospectiveSort(keys, comparer!); + } + catch (IndexOutOfRangeException) + { + SortUtils.ThrowArgumentException_BadComparer(comparer); + } + catch (Exception e) + { + ThrowHelper.ThrowInvalidOperationException(SortUtils.InvalidOperation_IComparerFailed, e); + } + } + + private static void SwapIfGreater(Span keys, Comparison comparer, int i, int j) + { + Debug.Assert(i != j); + + if (comparer(keys[i], keys[j]) > 0) + { + (keys[j], keys[i]) = (keys[i], keys[j]); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Swap(Span a, int i, int j) + { + Debug.Assert(i != j); + + (a[j], a[i]) = (a[i], a[j]); + } + + public static void IntrospectiveSort(Span keys, Comparison comparer) + { + Debug.Assert(comparer != null); + + if (keys.Length > 1) + { + IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1), comparer!); + } + } + + private static void IntroSort(Span keys, int depthLimit, Comparison comparer) + { + Debug.Assert(!keys.IsEmpty); + Debug.Assert(depthLimit >= 0); + Debug.Assert(comparer != null); + + var partitionSize = keys.Length; + while (partitionSize > 1) + { + if (partitionSize <= SortUtils.INTROSORT_SIZE_THRESHOLD) + { + + if (partitionSize == 2) + { + SwapIfGreater(keys, comparer!, 0, 1); + return; + } + + if (partitionSize == 3) + { + SwapIfGreater(keys, comparer!, 0, 1); + SwapIfGreater(keys, comparer!, 0, 2); + SwapIfGreater(keys, comparer!, 1, 2); + return; + } + + InsertionSort(keys[..partitionSize], comparer!); + return; + } + + if (depthLimit == 0) + { + HeapSort(keys[..partitionSize], comparer!); + return; + } + depthLimit--; + + var p = PickPivotAndPartition(keys[..partitionSize], comparer!); + + // Note we've already partitioned around the pivot and do not have to move the pivot again. + IntroSort(keys[(p + 1)..partitionSize], depthLimit, comparer!); + partitionSize = p; + } + } + + private static int PickPivotAndPartition(Span keys, Comparison comparer) + { + Debug.Assert(keys.Length >= SortUtils.INTROSORT_SIZE_THRESHOLD); + Debug.Assert(comparer != null); + + var hi = keys.Length - 1; + + // Compute median-of-three. But also partition them, since we've done the comparison. + var middle = hi >> 1; + + // Sort lo, mid and hi appropriately, then pick mid as the pivot. + SwapIfGreater(keys, comparer!, 0, middle); // swap the low with the mid point + SwapIfGreater(keys, comparer!, 0, hi); // swap the low with the high + SwapIfGreater(keys, comparer!, middle, hi); // swap the middle with the high + + var pivot = keys[middle]; + Swap(keys, middle, hi - 1); + int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below. + + while (left < right) + { + while (comparer!(keys[++left], pivot) < 0) + ; + while (comparer(pivot, keys[--right]) < 0) + ; + + if (left >= right) + break; + + Swap(keys, left, right); + } + + // Put pivot in the right location. + if (left != hi - 1) + { + Swap(keys, left, hi - 1); + } + return left; + } + + private static void HeapSort(Span keys, Comparison comparer) + { + Debug.Assert(comparer != null); + Debug.Assert(!keys.IsEmpty); + + var n = keys.Length; + for (var i = n >> 1; i >= 1; i--) + { + DownHeap(keys, i, n, comparer!); + } + + for (var i = n; i > 1; i--) + { + Swap(keys, 0, i - 1); + DownHeap(keys, 1, i - 1, comparer!); + } + } + + private static void DownHeap(Span keys, int i, int n, Comparison comparer) + { + Debug.Assert(comparer != null); + + var d = keys[i - 1]; + while (i <= n >> 1) + { + var child = 2 * i; + if (child < n && comparer!(keys[child - 1], keys[child]) < 0) + { + child++; + } + + if (!(comparer!(d, keys[child - 1]) < 0)) + break; + + keys[i - 1] = keys[child - 1]; + i = child; + } + + keys[i - 1] = d; + } + + private static void InsertionSort(Span keys, Comparison comparer) + { + for (var i = 0; i < keys.Length - 1; i++) + { + var t = keys[i + 1]; + + var j = i; + while (j >= 0 && comparer(t, keys[j]) < 0) + { + keys[j + 1] = keys[j]; + j--; + } + + keys[j + 1] = t; + } + } +} + +internal sealed class GenericArraySortHelper : IArraySortHelper + where T : IComparable +{ + // Do not add a constructor to this class because ArraySortHelper.CreateSortHelper will not execute it + + #region IArraySortHelper Members + + public void Sort(Span keys, IComparer? comparer) + { + try + { + if (comparer == null || comparer == Comparer.Default) + { + if (keys.Length > 1) + { + // For floating-point, do a pre-pass to move all NaNs to the beginning + // so that we can do an optimized comparison as part of the actual sort + // on the remainder of the values. + if (typeof(T) == typeof(double) || + typeof(T) == typeof(float)) + { + var nanLeft = SortUtils.MoveNansToFront(keys, default(Span)); + if (nanLeft == keys.Length) + { + return; + } + keys = keys[nanLeft..]; + } + + IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1)); + } + } + else + { + ArraySortHelper.IntrospectiveSort(keys, comparer.Compare); + } + } + catch (IndexOutOfRangeException) + { + SortUtils.ThrowArgumentException_BadComparer(comparer); + } + catch (Exception e) + { + ThrowHelper.ThrowInvalidOperationException(SortUtils.InvalidOperation_IComparerFailed, e); + } + } + + #endregion + + /// Swaps the values in the two references if the first is greater than the second. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void SwapIfGreater(ref T i, ref T j) + { + if (i != null && GreaterThan(ref i, ref j)) + { + Swap(ref i, ref j); + } + } + + /// Swaps the values in the two references, regardless of whether the two references are the same. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Swap(ref T i, ref T j) + { + Debug.Assert(!Unsafe.AreSame(ref i, ref j)); + + (j, i) = (i, j); + } + + private static void IntroSort(Span keys, int depthLimit) + { + Debug.Assert(!keys.IsEmpty); + Debug.Assert(depthLimit >= 0); + + var partitionSize = keys.Length; + while (partitionSize > 1) + { + if (partitionSize <= SortUtils.INTROSORT_SIZE_THRESHOLD) + { + if (partitionSize == 2) + { + SwapIfGreater(ref keys[0], ref keys[1]); + return; + } + + if (partitionSize == 3) + { + ref var hiRef = ref keys[2]; + ref var him1Ref = ref keys[1]; + ref var loRef = ref keys[0]; + + SwapIfGreater(ref loRef, ref him1Ref); + SwapIfGreater(ref loRef, ref hiRef); + SwapIfGreater(ref him1Ref, ref hiRef); + return; + } + + InsertionSort(keys[..partitionSize]); + return; + } + + if (depthLimit == 0) + { + HeapSort(keys[..partitionSize]); + return; + } + depthLimit--; + + var p = PickPivotAndPartition(keys[..partitionSize]); + + // Note we've already partitioned around the pivot and do not have to move the pivot again. + IntroSort(keys[(p + 1)..partitionSize], depthLimit); + partitionSize = p; + } + } + + private static int PickPivotAndPartition(Span keys) + { + Debug.Assert(keys.Length >= SortUtils.INTROSORT_SIZE_THRESHOLD); + + // Use median-of-three to select a pivot. Grab a reference to the 0th, Length-1th, and Length/2th elements, and sort them. + ref var zeroRef = ref MemoryMarshal.GetReference(keys); + ref var lastRef = ref Unsafe.Add(ref zeroRef, keys.Length - 1); + ref var middleRef = ref Unsafe.Add(ref zeroRef, (keys.Length - 1) >> 1); + SwapIfGreater(ref zeroRef, ref middleRef); + SwapIfGreater(ref zeroRef, ref lastRef); + SwapIfGreater(ref middleRef, ref lastRef); + + // Select the middle value as the pivot, and move it to be just before the last element. + ref var nextToLastRef = ref Unsafe.Add(ref zeroRef, keys.Length - 2); + var pivot = middleRef; + Swap(ref middleRef, ref nextToLastRef); + + // Walk the left and right pointers, swapping elements as necessary, until they cross. + ref T leftRef = ref zeroRef!, rightRef = ref nextToLastRef!; + while (Unsafe.IsAddressLessThan(ref leftRef, ref rightRef)) + { + if (pivot == null) + { + while (Unsafe.IsAddressLessThan(ref leftRef!, ref nextToLastRef) && (leftRef = ref Unsafe.Add(ref leftRef, 1)) == null) + ; + while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef) && (rightRef = ref Unsafe.Add(ref rightRef, -1)) != null) + ; + } + else + { + while (Unsafe.IsAddressLessThan(ref leftRef, ref nextToLastRef) && GreaterThan(ref pivot, ref leftRef = ref Unsafe.Add(ref leftRef, 1))) + ; + while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef) && LessThan(ref pivot, ref rightRef = ref Unsafe.Add(ref rightRef, -1))) + ; + } + + if (!Unsafe.IsAddressLessThan(ref leftRef, ref rightRef!)) + { + break; + } + + Swap(ref leftRef, ref rightRef); + } + + // Put the pivot in the correct location. + if (!Unsafe.AreSame(ref leftRef, ref nextToLastRef)) + { + Swap(ref leftRef, ref nextToLastRef); + } + return (int)((nint)Unsafe.ByteOffset(ref zeroRef, ref leftRef) / Unsafe.SizeOf()); + } + + private static void HeapSort(Span keys) + { + Debug.Assert(!keys.IsEmpty); + + var n = keys.Length; + for (var i = n >> 1; i >= 1; i--) + { + DownHeap(keys, i, n); + } + + for (var i = n; i > 1; i--) + { + Swap(ref keys[0], ref keys[i - 1]); + DownHeap(keys, 1, i - 1); + } + } + + private static void DownHeap(Span keys, int i, int n) + { + var d = keys[i - 1]; + while (i <= n >> 1) + { + var child = 2 * i; + if (child < n && (keys[child - 1] == null || LessThan(ref keys[child - 1], ref keys[child]))) + { + child++; + } + + if (keys[child - 1] == null || !LessThan(ref d, ref keys[child - 1])) + break; + + keys[i - 1] = keys[child - 1]; + i = child; + } + + keys[i - 1] = d; + } + + private static void InsertionSort(Span keys) + { + for (var i = 0; i < keys.Length - 1; i++) + { + var t = Unsafe.Add(ref MemoryMarshal.GetReference(keys), i + 1); + + var j = i; + while (j >= 0 && (t == null || LessThan(ref t, ref Unsafe.Add(ref MemoryMarshal.GetReference(keys), j)))) + { + Unsafe.Add(ref MemoryMarshal.GetReference(keys), j + 1) = Unsafe.Add(ref MemoryMarshal.GetReference(keys), j); + j--; + } + + Unsafe.Add(ref MemoryMarshal.GetReference(keys), j + 1) = t!; + } + } + + // - These methods exist for use in sorting, where the additional operations present in + // the CompareTo methods that would otherwise be used on these primitives add non-trivial overhead, + // in particular for floating point where the CompareTo methods need to factor in NaNs. + // - The floating-point comparisons here assume no NaNs, which is valid only because the sorting routines + // themselves special-case NaN with a pre-pass that ensures none are present in the values being sorted + // by moving them all to the front first and then sorting the rest. + // - These are duplicated here rather than being on a helper type due to current limitations around generic inlining. + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call + private static bool LessThan(ref T left, ref T right) + => typeof(T) == typeof(byte) + ? (byte)(object)left < (byte)(object)right + : typeof(T) == typeof(sbyte) + ? (sbyte)(object)left < (sbyte)(object)right + : typeof(T) == typeof(ushort) + ? (ushort)(object)left < (ushort)(object)right + : typeof(T) == typeof(short) + ? (short)(object)left < (short)(object)right + : typeof(T) == typeof(uint) + ? (uint)(object)left < (uint)(object)right + : typeof(T) == typeof(int) + ? (int)(object)left < (int)(object)right + : typeof(T) == typeof(ulong) + ? (ulong)(object)left < (ulong)(object)right + : typeof(T) == typeof(long) + ? (long)(object)left < (long)(object)right + : typeof(T) == typeof(nuint) + ? (nuint)(object)left < (nuint)(object)right + : typeof(T) == typeof(nint) + ? (nint)(object)left < (nint)(object)right + : typeof(T) == typeof(float) + ? (float)(object)left < (float)(object)right + : typeof(T) == typeof(double) + ? (double)(object)left < (double)(object)right + : left.CompareTo(right) < 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call + private static bool GreaterThan(ref T left, ref T right) + => typeof(T) == typeof(byte) + ? (byte)(object)left > (byte)(object)right + : typeof(T) == typeof(sbyte) + ? (sbyte)(object)left > (sbyte)(object)right + : typeof(T) == typeof(ushort) + ? (ushort)(object)left > (ushort)(object)right + : typeof(T) == typeof(short) + ? (short)(object)left > (short)(object)right + : typeof(T) == typeof(uint) + ? (uint)(object)left > (uint)(object)right + : typeof(T) == typeof(int) + ? (int)(object)left > (int)(object)right + : typeof(T) == typeof(ulong) + ? (ulong)(object)left > (ulong)(object)right + : typeof(T) == typeof(long) + ? (long)(object)left > (long)(object)right + : typeof(T) == typeof(nuint) + ? (nuint)(object)left > (nuint)(object)right + : typeof(T) == typeof(nint) + ? (nint)(object)left > (nint)(object)right + : typeof(T) == typeof(float) + ? (float)(object)left > (float)(object)right + : typeof(T) == typeof(double) + ? (double)(object)left > (double)(object)right + : left.CompareTo(right) > 0; +} + +#endregion + +#region ArraySortHelper for paired key and value arrays + +internal sealed class ArraySortHelper : IArraySortHelper +{ + public static IArraySortHelper Default { get; } = CreateArraySortHelper(); + + private static IArraySortHelper CreateArraySortHelper() + => typeof(IComparable).IsAssignableFrom(typeof(TKey)) + ? (IArraySortHelper)Activator.CreateInstance(typeof(GenericArraySortHelper<,>).MakeGenericType(typeof(TKey), typeof(TValue))) + : new ArraySortHelper(); + + public void Sort(Span keys, Span values, IComparer? comparer) + { + // Add a try block here to detect IComparers (or their + // underlying IComparables, etc) that are bogus. + try + { + IntrospectiveSort(keys, values, comparer ?? Comparer.Default); + } + catch (IndexOutOfRangeException) + { + SortUtils.ThrowArgumentException_BadComparer(comparer); + } + catch (Exception e) + { + ThrowHelper.ThrowInvalidOperationException(SortUtils.InvalidOperation_IComparerFailed, e); + } + } + + private static void SwapIfGreaterWithValues(Span keys, Span values, IComparer comparer, int i, int j) + { + Debug.Assert(comparer != null); + Debug.Assert(0 <= i && i < keys.Length && i < values.Length); + Debug.Assert(0 <= j && j < keys.Length && j < values.Length); + Debug.Assert(i != j); + + if (comparer!.Compare(keys[i], keys[j]) > 0) + { + (keys[j], keys[i]) = (keys[i], keys[j]); + + (values[j], values[i]) = (values[i], values[j]); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Swap(Span keys, Span values, int i, int j) + { + Debug.Assert(i != j); + + (keys[j], keys[i]) = (keys[i], keys[j]); + + (values[j], values[i]) = (values[i], values[j]); + } + + public static void IntrospectiveSort(Span keys, Span values, IComparer comparer) + { + Debug.Assert(comparer != null); + Debug.Assert(keys.Length == values.Length); + + if (keys.Length > 1) + { + IntroSort(keys, values, 2 * (BitOperations.Log2((uint)keys.Length) + 1), comparer!); + } + } + + public static void IntroSort(Span keys, Span values, int depthLimit, IComparer comparer) + { + Debug.Assert(!keys.IsEmpty); + Debug.Assert(values.Length == keys.Length); + Debug.Assert(depthLimit >= 0); + Debug.Assert(comparer != null); + + var partitionSize = keys.Length; + while (partitionSize > 1) + { + if (partitionSize <= SortUtils.INTROSORT_SIZE_THRESHOLD) + { + + if (partitionSize == 2) + { + SwapIfGreaterWithValues(keys, values, comparer!, 0, 1); + return; + } + + if (partitionSize == 3) + { + SwapIfGreaterWithValues(keys, values, comparer!, 0, 1); + SwapIfGreaterWithValues(keys, values, comparer!, 0, 2); + SwapIfGreaterWithValues(keys, values, comparer!, 1, 2); + return; + } + + InsertionSort(keys[..partitionSize], values[..partitionSize], comparer!); + return; + } + + if (depthLimit == 0) + { + HeapSort(keys[..partitionSize], values[..partitionSize], comparer!); + return; + } + depthLimit--; + + var p = PickPivotAndPartition(keys[..partitionSize], values[..partitionSize], comparer!); + + // Note we've already partitioned around the pivot and do not have to move the pivot again. + IntroSort(keys[(p + 1)..partitionSize], values[(p + 1)..partitionSize], depthLimit, comparer!); + partitionSize = p; + } + } + + private static int PickPivotAndPartition(Span keys, Span values, IComparer comparer) + { + Debug.Assert(keys.Length >= SortUtils.INTROSORT_SIZE_THRESHOLD); + Debug.Assert(comparer != null); + + var hi = keys.Length - 1; + + // Compute median-of-three. But also partition them, since we've done the comparison. + var middle = hi >> 1; + + // Sort lo, mid and hi appropriately, then pick mid as the pivot. + SwapIfGreaterWithValues(keys, values, comparer!, 0, middle); // swap the low with the mid point + SwapIfGreaterWithValues(keys, values, comparer!, 0, hi); // swap the low with the high + SwapIfGreaterWithValues(keys, values, comparer!, middle, hi); // swap the middle with the high + + var pivot = keys[middle]; + Swap(keys, values, middle, hi - 1); + int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below. + + while (left < right) + { + while (comparer!.Compare(keys[++left], pivot) < 0) + ; + while (comparer.Compare(pivot, keys[--right]) < 0) + ; + + if (left >= right) + break; + + Swap(keys, values, left, right); + } + + // Put pivot in the right location. + if (left != hi - 1) + { + Swap(keys, values, left, hi - 1); + } + return left; + } + + private static void HeapSort(Span keys, Span values, IComparer comparer) + { + Debug.Assert(comparer != null); + Debug.Assert(!keys.IsEmpty); + + var n = keys.Length; + for (var i = n >> 1; i >= 1; i--) + { + DownHeap(keys, values, i, n, comparer!); + } + + for (var i = n; i > 1; i--) + { + Swap(keys, values, 0, i - 1); + DownHeap(keys, values, 1, i - 1, comparer!); + } + } + + private static void DownHeap(Span keys, Span values, int i, int n, IComparer comparer) + { + Debug.Assert(comparer != null); + + var d = keys[i - 1]; + var dValue = values[i - 1]; + + while (i <= n >> 1) + { + var child = 2 * i; + if (child < n && comparer!.Compare(keys[child - 1], keys[child]) < 0) + { + child++; + } + + if (!(comparer!.Compare(d, keys[child - 1]) < 0)) + break; + + keys[i - 1] = keys[child - 1]; + values[i - 1] = values[child - 1]; + i = child; + } + + keys[i - 1] = d; + values[i - 1] = dValue; + } + + private static void InsertionSort(Span keys, Span values, IComparer comparer) + { + Debug.Assert(comparer != null); + + for (var i = 0; i < keys.Length - 1; i++) + { + var t = keys[i + 1]; + var tValue = values[i + 1]; + + var j = i; + while (j >= 0 && comparer!.Compare(t, keys[j]) < 0) + { + keys[j + 1] = keys[j]; + values[j + 1] = values[j]; + j--; + } + + keys[j + 1] = t; + values[j + 1] = tValue; + } + } +} + +internal sealed class GenericArraySortHelper : IArraySortHelper + where TKey : IComparable +{ + public void Sort(Span keys, Span values, IComparer? comparer) + { + // Add a try block here to detect IComparers (or their + // underlying IComparables, etc) that are bogus. + try + { + if (comparer == null || comparer == Comparer.Default) + { + if (keys.Length > 1) + { + // For floating-point, do a pre-pass to move all NaNs to the beginning + // so that we can do an optimized comparison as part of the actual sort + // on the remainder of the values. + if (typeof(TKey) == typeof(double) || + typeof(TKey) == typeof(float)) + { + var nanLeft = SortUtils.MoveNansToFront(keys, values); + if (nanLeft == keys.Length) + { + return; + } + keys = keys[nanLeft..]; + values = values[nanLeft..]; + } + + IntroSort(keys, values, 2 * (BitOperations.Log2((uint)keys.Length) + 1)); + } + } + else + { + ArraySortHelper.IntrospectiveSort(keys, values, comparer); + } + } + catch (IndexOutOfRangeException) + { + SortUtils.ThrowArgumentException_BadComparer(comparer); + } + catch (Exception e) + { + ThrowHelper.ThrowInvalidOperationException(SortUtils.InvalidOperation_IComparerFailed, e); + } + } + + private static void SwapIfGreaterWithValues(Span keys, Span values, int i, int j) + { + Debug.Assert(i != j); + + ref var keyRef = ref keys[i]; + if (keyRef != null && GreaterThan(ref keyRef, ref keys[j])) + { + var key = keyRef; + keys[i] = keys[j]; + keys[j] = key; + + (values[j], values[i]) = (values[i], values[j]); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Swap(Span keys, Span values, int i, int j) + { + Debug.Assert(i != j); + + (keys[j], keys[i]) = (keys[i], keys[j]); + + (values[j], values[i]) = (values[i], values[j]); + } + + private static void IntroSort(Span keys, Span values, int depthLimit) + { + Debug.Assert(!keys.IsEmpty); + Debug.Assert(values.Length == keys.Length); + Debug.Assert(depthLimit >= 0); + + var partitionSize = keys.Length; + while (partitionSize > 1) + { + if (partitionSize <= SortUtils.INTROSORT_SIZE_THRESHOLD) + { + + if (partitionSize == 2) + { + SwapIfGreaterWithValues(keys, values, 0, 1); + return; + } + + if (partitionSize == 3) + { + SwapIfGreaterWithValues(keys, values, 0, 1); + SwapIfGreaterWithValues(keys, values, 0, 2); + SwapIfGreaterWithValues(keys, values, 1, 2); + return; + } + + InsertionSort(keys[..partitionSize], values[..partitionSize]); + return; + } + + if (depthLimit == 0) + { + HeapSort(keys[..partitionSize], values[..partitionSize]); + return; + } + depthLimit--; + + var p = PickPivotAndPartition(keys[..partitionSize], values[..partitionSize]); + + // Note we've already partitioned around the pivot and do not have to move the pivot again. + IntroSort(keys[(p + 1)..partitionSize], values[(p + 1)..partitionSize], depthLimit); + partitionSize = p; + } + } + + private static int PickPivotAndPartition(Span keys, Span values) + { + Debug.Assert(keys.Length >= SortUtils.INTROSORT_SIZE_THRESHOLD); + + var hi = keys.Length - 1; + + // Compute median-of-three. But also partition them, since we've done the comparison. + var middle = hi >> 1; + + // Sort lo, mid and hi appropriately, then pick mid as the pivot. + SwapIfGreaterWithValues(keys, values, 0, middle); // swap the low with the mid point + SwapIfGreaterWithValues(keys, values, 0, hi); // swap the low with the high + SwapIfGreaterWithValues(keys, values, middle, hi); // swap the middle with the high + + var pivot = keys[middle]; + Swap(keys, values, middle, hi - 1); + int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below. + + while (left < right) + { + if (pivot == null) + { + while (left < (hi - 1) && keys[++left] == null) + ; + while (right > 0 && keys[--right] != null) + ; + } + else + { + while (GreaterThan(ref pivot, ref keys[++left])) + ; + while (LessThan(ref pivot, ref keys[--right])) + ; + } + + if (left >= right) + break; + + Swap(keys, values, left, right); + } + + // Put pivot in the right location. + if (left != hi - 1) + { + Swap(keys, values, left, hi - 1); + } + return left; + } + + private static void HeapSort(Span keys, Span values) + { + Debug.Assert(!keys.IsEmpty); + + var n = keys.Length; + for (var i = n >> 1; i >= 1; i--) + { + DownHeap(keys, values, i, n); + } + + for (var i = n; i > 1; i--) + { + Swap(keys, values, 0, i - 1); + DownHeap(keys, values, 1, i - 1); + } + } + + private static void DownHeap(Span keys, Span values, int i, int n) + { + var d = keys[i - 1]; + var dValue = values[i - 1]; + + while (i <= n >> 1) + { + var child = 2 * i; + if (child < n && (keys[child - 1] == null || LessThan(ref keys[child - 1], ref keys[child]))) + { + child++; + } + + if (keys[child - 1] == null || !LessThan(ref d, ref keys[child - 1])) + break; + + keys[i - 1] = keys[child - 1]; + values[i - 1] = values[child - 1]; + i = child; + } + + keys[i - 1] = d; + values[i - 1] = dValue; + } + + private static void InsertionSort(Span keys, Span values) + { + for (var i = 0; i < keys.Length - 1; i++) + { + var t = keys[i + 1]; + var tValue = values[i + 1]; + + var j = i; + while (j >= 0 && (t == null || LessThan(ref t, ref keys[j]))) + { + keys[j + 1] = keys[j]; + values[j + 1] = values[j]; + j--; + } + + keys[j + 1] = t!; + values[j + 1] = tValue; + } + } + + // - These methods exist for use in sorting, where the additional operations present in + // the CompareTo methods that would otherwise be used on these primitives add non-trivial overhead, + // in particular for floating point where the CompareTo methods need to factor in NaNs. + // - The floating-point comparisons here assume no NaNs, which is valid only because the sorting routines + // themselves special-case NaN with a pre-pass that ensures none are present in the values being sorted + // by moving them all to the front first and then sorting the rest. + // - These are duplicated here rather than being on a helper type due to current limitations around generic inlining. + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call + private static bool LessThan(ref TKey left, ref TKey right) + => typeof(TKey) == typeof(byte) + ? (byte)(object)left < (byte)(object)right + : typeof(TKey) == typeof(sbyte) + ? (sbyte)(object)left < (sbyte)(object)right + : typeof(TKey) == typeof(ushort) + ? (ushort)(object)left < (ushort)(object)right + : typeof(TKey) == typeof(short) + ? (short)(object)left < (short)(object)right + : typeof(TKey) == typeof(uint) + ? (uint)(object)left < (uint)(object)right + : typeof(TKey) == typeof(int) + ? (int)(object)left < (int)(object)right + : typeof(TKey) == typeof(ulong) + ? (ulong)(object)left < (ulong)(object)right + : typeof(TKey) == typeof(long) + ? (long)(object)left < (long)(object)right + : typeof(TKey) == typeof(nuint) + ? (nuint)(object)left < (nuint)(object)right + : typeof(TKey) == typeof(nint) + ? (nint)(object)left < (nint)(object)right + : typeof(TKey) == typeof(float) + ? (float)(object)left < (float)(object)right + : typeof(TKey) == typeof(double) + ? (double)(object)left < (double)(object)right + : left.CompareTo(right) < 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] // compiles to a single comparison or method call + private static bool GreaterThan(ref TKey left, ref TKey right) + => typeof(TKey) == typeof(byte) + ? (byte)(object)left > (byte)(object)right + : typeof(TKey) == typeof(sbyte) + ? (sbyte)(object)left > (sbyte)(object)right + : typeof(TKey) == typeof(ushort) + ? (ushort)(object)left > (ushort)(object)right + : typeof(TKey) == typeof(short) + ? (short)(object)left > (short)(object)right + : typeof(TKey) == typeof(uint) + ? (uint)(object)left > (uint)(object)right + : typeof(TKey) == typeof(int) + ? (int)(object)left > (int)(object)right + : typeof(TKey) == typeof(ulong) + ? (ulong)(object)left > (ulong)(object)right + : typeof(TKey) == typeof(long) + ? (long)(object)left > (long)(object)right + : typeof(TKey) == typeof(nuint) + ? (nuint)(object)left > (nuint)(object)right + : typeof(TKey) == typeof(nint) + ? (nint)(object)left > (nint)(object)right + : typeof(TKey) == typeof(float) + ? (float)(object)left > (float)(object)right + : typeof(TKey) == typeof(double) + ? (double)(object)left > (double)(object)right + : left.CompareTo(right) > 0; +} + +#endregion + +/// Helper methods for use in array/span sorting routines. +internal static class SortUtils +{ + public static int MoveNansToFront(Span keys, Span values) where TKey : notnull + { + Debug.Assert(typeof(TKey) == typeof(double) || typeof(TKey) == typeof(float)); + + var left = 0; + + for (var i = 0; i < keys.Length; i++) + { + if ((typeof(TKey) == typeof(double) && double.IsNaN((double)(object)keys[i])) || + (typeof(TKey) == typeof(float) && float.IsNaN((float)(object)keys[i]))) + { + (keys[i], keys[left]) = (keys[left], keys[i]); + + if ((uint)i < (uint)values.Length) // check to see if we have values + { + (values[i], values[left]) = (values[left], values[i]); + } + + left++; + } + } + + return left; + } + + // This is the threshold where Introspective sort switches to Insertion sort. + // Empirically, 16 seems to speed up most cases without slowing down others, at least for integers. + // Large value types may benefit from a smaller number. + internal const int INTROSORT_SIZE_THRESHOLD = 16; + + [DoesNotReturn] + internal static void ThrowArgumentException_BadComparer(object? comparer) + => throw new ArgumentException(string.Format(Arg_BogusIComparer, comparer)); + + internal static string InvalidOperation_IComparerFailed => "Failed to compare two elements in the array."; + internal static string Arg_BogusIComparer + => "Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: '{0}'."; +} + +internal interface IArraySortHelper +{ + void Sort(Span keys, Span values, IComparer? comparer); +} + +internal interface IArraySortHelper +{ + void Sort(Span keys, IComparer? comparer); +} + +internal static class BitOperations +{ + private static ReadOnlySpan Log2DeBruijn + => new byte[32] + { + 00, 09, 01, 10, 13, 21, 02, 29, + 11, 14, 16, 18, 22, 25, 03, 30, + 08, 12, 20, 28, 15, 17, 24, 07, + 19, 27, 23, 06, 26, 05, 04, 31 + }; + + /// + /// Returns the integer (floor) log of the specified value, base 2. + /// Note that by convention, input value 0 returns 0 since log(0) is undefined. + /// + /// The value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + //[CLSCompliant(false)] + public static int Log2(uint value) + { + // The 0->0 contract is fulfilled by setting the LSB to 1. + // Log(1) is 0, and setting the LSB for values > 1 does not change the log2 result. + value |= 1; + +#if probablyNotSupportedOnUnity + // value lzcnt actual expected + // ..0001 31 31-31 0 + // ..0010 30 31-30 1 + // 0010.. 2 31-2 29 + // 0100.. 1 31-1 30 + // 1000.. 0 31-0 31 + if (Lzcnt.IsSupported) + { + return 31 ^ (int)Lzcnt.LeadingZeroCount(value); + } + + if (ArmBase.IsSupported) + { + return 31 ^ ArmBase.LeadingZeroCount(value); + } + + // BSR returns the log2 result directly. However BSR is slower than LZCNT + // on AMD processors, so we leave it as a fallback only. + if (X86Base.IsSupported) + { + return (int)X86Base.BitScanReverse(value); + } +#endif + + // Fallback contract is 0->0 + return Log2SoftwareFallback(value); + } + + /// + /// Returns the integer (floor) log of the specified value, base 2. + /// Note that by convention, input value 0 returns 0 since Log(0) is undefined. + /// Does not directly use any hardware intrinsics, nor does it incur branching. + /// + /// The value. + private static int Log2SoftwareFallback(uint value) + { + // No AggressiveInlining due to large method size + // Has conventional contract 0->0 (Log(0) is undefined) + + // Fill trailing zeros with ones, eg 00010010 becomes 00011111 + value |= value >> 01; + value |= value >> 02; + value |= value >> 04; + value |= value >> 08; + value |= value >> 16; + + // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check + return Unsafe.AddByteOffset( + // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u + ref MemoryMarshal.GetReference(Log2DeBruijn), + // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here + (IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); + } +} \ No newline at end of file diff --git a/Source/FisheryLib/BoolExtensions.cs b/Source/FisheryLib/BoolExtensions.cs new file mode 100644 index 0000000..5942908 --- /dev/null +++ b/Source/FisheryLib/BoolExtensions.cs @@ -0,0 +1,77 @@ +// 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. + +// https://github.com/CommunityToolkit/dotnet/blob/main/CommunityToolkit.HighPerformance/Extensions/BoolExtensions.cs + +using System.Runtime.CompilerServices; + +namespace FisheryLib; + +/// +/// 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. + [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. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int ToBitwiseMask32(this bool flag) + { + bool copy = flag; + byte rangeFlag = *(byte*)© + int negativeFlag = rangeFlag - 1; + int 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 . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe long ToBitwiseMask64(this bool flag) + { + bool copy = flag; + byte rangeFlag = *(byte*)© + long negativeFlag = (long)rangeFlag - 1; + long mask = ~negativeFlag; + + return mask; + } +} \ No newline at end of file diff --git a/Source/FisheryLib/CollectionExtensions.cs b/Source/FisheryLib/CollectionExtensions.cs new file mode 100644 index 0000000..136f7de --- /dev/null +++ b/Source/FisheryLib/CollectionExtensions.cs @@ -0,0 +1,333 @@ +// Copyright (c) 2022 bradson +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace FisheryLib; +public static class CollectionExtensions +{ + public static bool TryGetItem(this List list, int index, [NotNullWhen(true)] out T? item) + { + if (list.Count > index) + { + item = list[index]!; + return true; + } + + item = default; + return false; + } + + /// + /// Taken from Span<>.this[] and slower than List<>.this[] due to the ArrayAdjustment + /// + internal static T GetItemUnchecked(this List list, int index) + => Unsafe.Add(ref Unsafe.AddByteOffset(ref Unsafe.As>(list._items).Data, PerTypeValues.ArrayAdjustment), index)!; + + [StructLayout(LayoutKind.Sequential)] + private sealed class Pinnable + { + public T? Data; + } + private static class PerTypeValues + { + public static readonly IntPtr ArrayAdjustment = MeasureArrayAdjustment(); + private static IntPtr MeasureArrayAdjustment() + { + var array = new T[1]; + return Unsafe.ByteOffset(ref Unsafe.As>(array).Data!, ref array[0]); + } + } + + public static bool TryGetItem(this T[] array, int index, out T? item) + { + if (array.Length > index) + { + item = array[index]; + return true; + } + + item = default; + return false; + } + + public static T[] ToArray(this IEnumerable enumerable, int length) + { + var array = new T[length]; + + if (enumerable is IList ilist) + array.Fill(ilist); + else + array.FillUsingEnumerable(enumerable); + + return array; + } + + public static Telement[] ToArray(this Tenumerable enumerable, Telement[] destination) + where Tenumerable : IList + { + Guard.IsNotNull(destination); + + destination.Fill(enumerable); + return destination; + } + + public static T[] ToArray(this IEnumerable enumerable, T[] destination) + { + Guard.IsNotNull(destination); + + if (enumerable is IList ilist) + destination.Fill(ilist); + else + destination.FillUsingEnumerable(enumerable); + + return destination; + } + + private static void FillUsingEnumerable(this T[] array, IEnumerable enumerable, int startIndex = 0, int count = -1) + { + var i = 0u; + foreach (var item in enumerable) + { + if (i >= (uint)count || startIndex >= array.Length) + break; + + array[startIndex++] = item; + i++; + } + } + + public static void Fill(this Telement[] array, Tcollection collection, int startIndex = 0, int count = -1) + where Tcollection : IList + { + Guard.IsNotNull(collection); + + if (count <= 0) + count = collection.Count; + + if (count <= array.Length - startIndex) + { + collection.CopyTo(array, startIndex); + return; + } + + for (var listIndex = 0; listIndex < count && startIndex < array.Length; startIndex++, listIndex++) + array[startIndex] = collection[listIndex]; + } + + public static void Fill(this T[] array, IEnumerable enumerable, int startIndex = 0, int count = -1) + { + Guard.IsNotNull(enumerable); + + if (enumerable is IList ilist) + array.Fill(ilist, startIndex, count); + else + array.FillUsingEnumerable(enumerable, startIndex, count); + } + + public static ref TValue GetReference(this Dictionary dictionary, TKey key) + { + if (dictionary.buckets != null) + { + var keyCode = HashCode.Get(key) & 0x7FFFFFFF; // HashCode.Get can handle null, & 0x7FFFFFFF removes any sign + + var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length]; + while (bucket >= 0) + { + ref var entry = ref dictionary.entries[bucket]; + if (entry.hashCode == keyCode && entry.key.Equals(key)) + return ref entry.value; + + bucket = entry.next; + } + } + return ref ThrowKeyNotFoundException(key); + } + + /// + /// Returns a reference to the value field of an entry if the key exists and an Unsafe.NullRef{TValue} if not. + /// Must be checked with Unsafe.IsNullRef before assigning to a regular var or field without ref. + /// + /// The key + /// ref TValue or Unsafe.NullRef{TValue} + public static ref TValue TryGetReferenceUnsafe(this Dictionary dictionary, TKey key) + { + if (dictionary.buckets != null) + { + var keyCode = HashCode.Get(key) & 0x7FFFFFFF; + + var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length]; + while (bucket >= 0) + { + ref var entry = ref dictionary.entries[bucket]; + if (entry.hashCode == keyCode && entry.key.Equals(key)) + return ref entry.value; + + bucket = entry.next; + } + } + return ref Unsafe.NullRef(); + } + + public static ref TValue TryGetReferenceUnsafe(this Dictionary dictionary, ref TKey key) + where TKey : struct + { + if (dictionary.buckets != null) + { + var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF; + + var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length]; + while (bucket >= 0) + { + ref var entry = ref dictionary.entries[bucket]; + if (entry.hashCode == keyCode && entry.key.Equals(ref key)) + return ref entry.value; + + bucket = entry.next; + } + } + return ref Unsafe.NullRef(); + } + + public static TValue? TryGetValue(this Dictionary dictionary, ref TKey key) + { + if (dictionary.buckets != null) + { + var keyCode = HashCode.Get(ref key) & 0x7FFFFFFF; + + var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length]; + while (bucket >= 0) + { + ref var entry = ref dictionary.entries[bucket]; + if (entry.hashCode == keyCode && Equality.EqualsByRef(ref entry.key, ref key)) + return entry.value; + + bucket = entry.next; + } + } + return default; + } + + [return: MaybeNull] + public static TValue TryGetValue(this Dictionary dictionary, TKey key) + { + if (dictionary.buckets != null) + { + var keyCode = HashCode.Get(key) & 0x7FFFFFFF; + + var bucket = dictionary.buckets[keyCode % dictionary.buckets.Length]; + while (bucket >= 0) + { + ref var entry = ref dictionary.entries[bucket]; + if (entry.hashCode == keyCode && entry.key.Equals(key)) + return entry.value; + + bucket = entry.next; + } + } + return default; + } + + [DoesNotReturn] + private static ref V ThrowKeyNotFoundException(T key) + { + Guard.IsNotNull(key); + ThrowKeyNotFoundException(key); + return ref Unsafe.NullRef(); + } + + [DoesNotReturn] + private static void ThrowKeyNotFoundException(object key) + => throw new KeyNotFoundException( + string.Format("The given key '{0}' was not present in the dictionary.", key)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ref int Version(this List list) => ref list._version; + + /// + /// Returns a list's internal _items array. Keep in mind list.Version() needs to change every time items are modified. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T[] ItemsUnchecked(this List list) => list._items; + + /// + /// Returns a span pointing to a list's internal _items array. Keep in mind list.Version() needs to change every time items are modified. + /// + public static Span AsSpanUnchecked(this List list) => new(list._items, 0, list.Count); + + public static ReadOnlySpan AsReadOnlySpan(this List list) => new(list._items, 0, list.Count); + + public static void ReplaceContentsWith(this List list, V collection) where V : ICollection + { + if (ReferenceEquals(list, collection)) + return; + + if (collection is null + || (collection.Count is var collectionCount + && collectionCount == 0)) + { + list.Clear(); + return; + } + + if (collectionCount <= list.Count) + { + if (collectionCount < list.Count) + list.RemoveRange(collectionCount, list.Count - collectionCount); + collection.CopyTo(list._items, 0); + } + else + { + list._items = new T[collectionCount]; + collection.CopyTo(list._items, 0); + list._size = collectionCount; + } + list._version++; + } + + public static IList TryNew(IEnumerable enumerable, Type type) => GetListExtensionMethods(type).New(enumerable); + + public static void TryAddRange(this IList list, T collection, Type type) where T : ICollection => GetListExtensionMethods(type).AddRange(list, collection); + + public static Array TryToArray(this IList list, Type type) => GetListExtensionMethods(type).ToArray(list); + + public static GenericBase GetListExtensionMethods(Type type) + => ListExtensionCache.TryGetValue(type) + ?? CreateListExtensionMethods(type); + + private static GenericBase CreateListExtensionMethods(Type type) + { + var methods = (GenericBase)Activator.CreateInstance(typeof(Generic<>).MakeGenericType(type)); + ListExtensionCache[type] = methods; + return methods; + } + + private static Dictionary ListExtensionCache => _listExtensionCache ??= new(); + + [ThreadStatic] + private static Dictionary? _listExtensionCache; + + public abstract class GenericBase + { + public abstract void AddRange(IList list, ICollection collection); + public abstract Array ToArray(IList list); + public abstract IList New(IEnumerable enumerable); + } + + public class Generic : GenericBase + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override void AddRange(IList list, ICollection collection) => ((List)list).AddRange((IEnumerable)collection); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override Array ToArray(IList list) => ((List)list).ToArray(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override IList New(IEnumerable enumerable) => new List((IEnumerable)enumerable); + } +} \ No newline at end of file diff --git a/Source/FisheryLib/Convert.cs b/Source/FisheryLib/Convert.cs new file mode 100644 index 0000000..40c8fe8 --- /dev/null +++ b/Source/FisheryLib/Convert.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2022 bradson +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace FisheryLib; +public static class Convert +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe TTo To(TFrom from) where TFrom : IConvertible + => FunctionPointers.Convert.To.Default(from, CultureInfo.InvariantCulture); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe TTo To(TFrom from, IFormatProvider provider) where TFrom : IConvertible + => FunctionPointers.Convert.To.Default(from, provider); +} \ No newline at end of file diff --git a/Source/FisheryLib/Diagnostics/Attributes/CallerArgumentExpressionAttribute.cs b/Source/FisheryLib/Diagnostics/Attributes/CallerArgumentExpressionAttribute.cs new file mode 100644 index 0000000..ba668ba --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Attributes/CallerArgumentExpressionAttribute.cs @@ -0,0 +1,24 @@ +// 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.Runtime.CompilerServices; + +/// +/// An attribute that allows parameters to receive the expression of other parameters. +/// +/// Internal copy from the BCL attribute. +[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] +internal sealed class CallerArgumentExpressionAttribute : Attribute +{ + /// + /// Initializes a new instance of the class. + /// + /// The condition parameter value. + public CallerArgumentExpressionAttribute(string parameterName) => ParameterName = parameterName; + + /// + /// Gets the parameter name the expression is retrieved from. + /// + public string ParameterName { get; } +} \ No newline at end of file diff --git a/Source/FisheryLib/Diagnostics/Extensions/TypeExtensions.cs b/Source/FisheryLib/Diagnostics/Extensions/TypeExtensions.cs new file mode 100644 index 0000000..aacaa23 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Extensions/TypeExtensions.cs @@ -0,0 +1,162 @@ +// 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 System.Runtime.CompilerServices; + +namespace FisheryLib.Utility.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(); + + /// + /// Returns a simple string representation of a type. + /// + /// The input type. + /// The string representation of . + 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 var 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 tuples. 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 + return type.IsNested + ? $"{FormatDisplayString(type.DeclaringType!, genericTypeOffset, typeArguments)}.{displayName}" + : $"{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) + { + var 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()); + }); + } +} diff --git a/Source/FisheryLib/Diagnostics/Extensions/ValueTypeExtensions.cs b/Source/FisheryLib/Diagnostics/Extensions/ValueTypeExtensions.cs new file mode 100644 index 0000000..54511d9 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Extensions/ValueTypeExtensions.cs @@ -0,0 +1,66 @@ +// 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 FisheryLib.Utility.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" + /// + /// + public static unsafe string ToHexString(this T value) + where T : unmanaged + { + var sizeOfT = Unsafe.SizeOf(); + var bufferSize = (2 * sizeOfT) + 2; + var p = stackalloc char[bufferSize]; + + p[0] = '0'; + p[1] = 'x'; + + ref var rh = ref HexCharactersTable.DangerousGetPinnableReference(); + + for (int i = 0, j = bufferSize - 2; i < sizeOfT; i++, j -= 2) + { + var b = ((byte*)&value)[i]; + var low = b & 0x0F; + var high = (b & 0xF0) >> 4; + + p[j + 1] = (char)Unsafe.Add(ref rh, low); + p[j] = (char)Unsafe.Add(ref rh, high); + } + + return new(p, 0, bufferSize); + } +} diff --git a/Source/FisheryLib/Diagnostics/Generated/Guard.Collection.g.cs b/Source/FisheryLib/Diagnostics/Generated/Guard.Collection.g.cs new file mode 100644 index 0000000..cab243c --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/Guard.Collection.g.cs @@ -0,0 +1,1822 @@ +// 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.Runtime.CompilerServices; + +namespace FisheryLib.Utility.Diagnostics; + +/// +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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("span")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("memory")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("array")] 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, [CallerArgumentExpression("array")] 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, [CallerArgumentExpression("array")] 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, [CallerArgumentExpression("array")] 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, [CallerArgumentExpression("array")] 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, [CallerArgumentExpression("array")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("list")] 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, [CallerArgumentExpression("list")] 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, [CallerArgumentExpression("list")] 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, [CallerArgumentExpression("list")] 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, [CallerArgumentExpression("list")] 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, [CallerArgumentExpression("list")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("collection")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] string name = "") + { + if ((uint)index >= (uint)collection.Count) + { + return; + } + + ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, collection, name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Generated/Guard.Collection.tt b/Source/FisheryLib/Diagnostics/Generated/Guard.Collection.tt new file mode 100644 index 0000000..4784e18 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/Guard.Collection.tt @@ -0,0 +1,296 @@ +// 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.Runtime.CompilerServices; + +namespace PerformanceFish.Utility.Diagnostics; + +/// +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#>, [CallerArgumentExpression("<#=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#>, [CallerArgumentExpression("<#=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, [CallerArgumentExpression("<#=item.Name#>")] 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, [CallerArgumentExpression("<#=item.Name#>")] 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, [CallerArgumentExpression("<#=item.Name#>")] 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, [CallerArgumentExpression("<#=item.Name#>")] 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, [CallerArgumentExpression("<#=item.Name#>")] 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, [CallerArgumentExpression("<#=item.Name#>")] 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, [CallerArgumentExpression("source")] 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, [CallerArgumentExpression("source")] 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#>, [CallerArgumentExpression("index")] string name = "") + { +<# +// Here we're leveraging the fact that signed integers are represented +// in 2-complement to perform the bounds check with a single compare operation. +// This is the same trick used throughout CoreCLR as well. +// For more info and code sample, see the original conversation here: +// https://github.com/CommunityToolkit/WindowsCommunityToolkit/pull/3131#discussion_r390682835 +#> + 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#>, [CallerArgumentExpression("index")] string name = "") + { + if ((uint)index >= (uint)<#=item.Name#>.<#=item.Size#>) + { + return; + } + + ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, <#=item.Cast#><#=item.Name#>, name); + } +<# +}); +#> +} diff --git a/Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.g.cs b/Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.g.cs new file mode 100644 index 0000000..d8d48b7 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.g.cs @@ -0,0 +1,3528 @@ +// 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.Runtime.CompilerServices; + +namespace FisheryLib.Utility.Diagnostics; + +/// +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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] string name = "") + { + if (value < minimum || value > maximum) + { + return; + } + + ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.tt b/Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.tt new file mode 100644 index 0000000..23b4af2 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/Guard.Comparable.Numeric.tt @@ -0,0 +1,270 @@ +// 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.Runtime.CompilerServices; + +namespace PerformanceFish.Utility.Diagnostics; + +/// +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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] string name = "") + { + if (value < minimum || value > maximum) + { + return; + } + + ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); + } +<# +}); +#> +} diff --git a/Source/FisheryLib/Diagnostics/Generated/Guard.md b/Source/FisheryLib/Diagnostics/Generated/Guard.md new file mode 100644 index 0000000..b787eda --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/Guard.md @@ -0,0 +1,17 @@ +# T4 templates and generated APIs + +This folder contains a number of template files (with the `.tt` or `.ttinclude` extensions) and the generated `.g.cs` files generated by those templates. The template files use the T4 format, which is natively supported by Visual Studio (more info is available [here](https://docs.microsoft.com/visualstudio/modeling/code-generation-and-t4-text-templates)). + +## Why is this needed? + +There are a few reasons why T4 templates are used for the `Guard` class: + +- Especially when many overloads are available for an API, using templates makes it much easier to maintain code and spot mistakes, as the actual number of lines of code to review is much smaller: it's just the same code from each template! +- Using type-specific overloads instead of generic methods can result in faster code. For instance, T4 templates are used to generate overloads for comparison APIs (eg. `Guard.IsGreaterThan(int, int, string)`). This results in more compact and optimized code as opposed to a generic method using `where T : IComparable` 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/Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.g.cs b/Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.g.cs new file mode 100644 index 0000000..ec0fd7a --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.g.cs @@ -0,0 +1,810 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace FisheryLib.Utility.Diagnostics; + +/// +partial class Guard +{ + /// + 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/Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.tt b/Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.tt new file mode 100644 index 0000000..86e7aa1 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/ThrowHelper.Collection.tt @@ -0,0 +1,121 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace PerformanceFish.Utility.Diagnostics; + +/// +partial class Guard +{ + /// + 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/Source/FisheryLib/Diagnostics/Generated/TypeInfo.ttinclude b/Source/FisheryLib/Diagnostics/Generated/TypeInfo.ttinclude new file mode 100644 index 0000000..b2421dc --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Generated/TypeInfo.ttinclude @@ -0,0 +1,116 @@ +<#@ 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/Source/FisheryLib/Diagnostics/Guard.Boolean.cs b/Source/FisheryLib/Diagnostics/Guard.Boolean.cs new file mode 100644 index 0000000..7d38f1e --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Guard.Boolean.cs @@ -0,0 +1,356 @@ +// 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.Runtime.CompilerServices; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public static partial class Guard +{ + /// + /// 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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); + } + +#if NET6_0_OR_GREATER + /// + /// 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, [InterpolatedStringHandlerArgument("value")] ref IsTrueInterpolatedStringHandler message) + { + if (value) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForIsTrue(name, message.ToStringAndClear()); + } + + /// + /// 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, [InterpolatedStringHandlerArgument("value")] ref IsFalseInterpolatedStringHandler message) + { + if (!value) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForIsFalse(name, message.ToStringAndClear()); + } + + /// + /// Provides an interpolated string handler for that only performs formatting if the assert fails. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public struct IsTrueInterpolatedStringHandler + { + /// + /// The handler used to perform the formatting. + /// + private StringBuilder.AppendInterpolatedStringHandler handler; + + /// + /// The underlying instance used by , if any. + /// + private StringBuilder? builder; + + /// + /// Creates an instance of the handler. + /// + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The condition passed to the method. + /// A value indicating whether formatting should proceed. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public IsTrueInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) + { + if (condition) + { + this.handler = default; + this.builder = null; + + shouldAppend = false; + } + else + { + StringBuilder builder = new(); + + this.handler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder); + this.builder = builder; + + shouldAppend = true; + } + } + + /// + /// Extracts the built string from the handler. + /// + internal string ToStringAndClear() + { + string message = this.builder?.ToString() ?? string.Empty; + + this = default; + + return message; + } + + /// + public void AppendLiteral(string value) + { + this.handler.AppendLiteral(value); + } + + /// + public void AppendFormatted(T value) + { + this.handler.AppendFormatted(value); + } + + /// + public void AppendFormatted(T value, string? format) + { + this.handler.AppendFormatted(value, format); + } + + /// + public void AppendFormatted(T value, int alignment) + { + this.handler.AppendFormatted(value, alignment); + } + + /// + public void AppendFormatted(T value, int alignment, string? format) + { + this.handler.AppendFormatted(value, alignment, format); + } + + /// + public void AppendFormatted(ReadOnlySpan value) + { + this.handler.AppendFormatted(value); + } + + /// + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + this.handler.AppendFormatted(value, alignment, format); + } + + /// + public void AppendFormatted(string? value) + { + this.handler.AppendFormatted(value); + } + + /// + public void AppendFormatted(string? value, int alignment = 0, string? format = null) + { + this.handler.AppendFormatted(value, alignment, format); + } + + /// + public void AppendFormatted(object? value, int alignment = 0, string? format = null) + { + this.handler.AppendFormatted(value, alignment, format); + } + } + + /// + /// Provides an interpolated string handler for that only performs formatting if the assert fails. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [InterpolatedStringHandler] + public struct IsFalseInterpolatedStringHandler + { + /// + /// The handler used to perform the formatting. + /// + private StringBuilder.AppendInterpolatedStringHandler handler; + + /// + /// The underlying instance used by , if any. + /// + private StringBuilder? builder; + + /// + /// Creates an instance of the handler. + /// + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// The condition passed to the method. + /// A value indicating whether formatting should proceed. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public IsFalseInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) + { + if (!condition) + { + this.handler = default; + this.builder = null; + + shouldAppend = false; + } + else + { + StringBuilder builder = new(); + + this.handler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder); + this.builder = builder; + + shouldAppend = true; + } + } + + /// + /// Extracts the built string from the handler. + /// + internal string ToStringAndClear() + { + string message = this.builder?.ToString() ?? string.Empty; + + this = default; + + return message; + } + + /// + public void AppendLiteral(string value) + { + this.handler.AppendLiteral(value); + } + + /// + public void AppendFormatted(T value) + { + this.handler.AppendFormatted(value); + } + + /// + public void AppendFormatted(T value, string? format) + { + this.handler.AppendFormatted(value, format); + } + + /// + public void AppendFormatted(T value, int alignment) + { + this.handler.AppendFormatted(value, alignment); + } + + /// + public void AppendFormatted(T value, int alignment, string? format) + { + this.handler.AppendFormatted(value, alignment, format); + } + + /// + public void AppendFormatted(ReadOnlySpan value) + { + this.handler.AppendFormatted(value); + } + + /// + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + this.handler.AppendFormatted(value, alignment, format); + } + + /// + public void AppendFormatted(string? value) + { + this.handler.AppendFormatted(value); + } + + /// + public void AppendFormatted(string? value, int alignment = 0, string? format = null) + { + this.handler.AppendFormatted(value, alignment, format); + } + + /// + public void AppendFormatted(object? value, int alignment = 0, string? format = null) + { + this.handler.AppendFormatted(value, alignment, format); + } + } +#endif +} diff --git a/Source/FisheryLib/Diagnostics/Guard.Comparable.Generic.cs b/Source/FisheryLib/Diagnostics/Guard.Comparable.Generic.cs new file mode 100644 index 0000000..80c2ad3 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Guard.Comparable.Generic.cs @@ -0,0 +1,439 @@ +// 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 FisheryLib.Utility.Diagnostics; + +/// +public 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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) + { + var valueByte = Unsafe.As(ref value); + var targetByte = Unsafe.As(ref target); + + if (valueByte == targetByte) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); + } + else if (sizeof(T) == 2) + { + var valueUShort = Unsafe.As(ref value); + var targetUShort = Unsafe.As(ref target); + + if (valueUShort == targetUShort) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); + } + else if (sizeof(T) == 4) + { + var valueUInt = Unsafe.As(ref value); + var targetUInt = Unsafe.As(ref target); + + if (valueUInt == targetUInt) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); + } + else if (sizeof(T) == 8) + { + var valueULong = Unsafe.As(ref value); + var targetULong = Unsafe.As(ref target); + + if (Bit64Compare(ref valueULong, ref targetULong)) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForBitwiseEqualTo(value, target, name); + } + else if (sizeof(T) == 16) + { + var valueULong0 = Unsafe.As(ref value); + var targetULong0 = Unsafe.As(ref target); + + if (Bit64Compare(ref valueULong0, ref targetULong0)) + { + var valueULong1 = Unsafe.Add(ref Unsafe.As(ref value), 1); + var 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(Unsafe.AsPointer(ref value), sizeof(T)); + Span targetBytes = new(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 + [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 var r0 = ref Unsafe.As(ref left); + ref var 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] string name = "") + where T : notnull, IComparable + { + if (value.CompareTo(minimum) < 0 || value.CompareTo(maximum) > 0) + { + return; + } + + ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotBetweenOrEqualTo(value, minimum, maximum, name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Guard.Comparable.Numeric.cs b/Source/FisheryLib/Diagnostics/Guard.Comparable.Numeric.cs new file mode 100644 index 0000000..a208ad1 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Guard.Comparable.Numeric.cs @@ -0,0 +1,213 @@ +// 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 FisheryLib.Utility.Diagnostics; + +/// +public 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, [CallerArgumentExpression("value")] string name = "") + { + var difference = value >= target ? (uint)(value - target) : (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, [CallerArgumentExpression("value")] string name = "") + { + var difference = value >= target ? (uint)(value - target) : (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, [CallerArgumentExpression("value")] string name = "") + { + var difference = value >= target ? (ulong)(value - target) : (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, [CallerArgumentExpression("value")] string name = "") + { + var difference = value >= target ? (ulong)(value - target) : (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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] string name = "") + { + var difference = value >= target ? (nuint)(value - target) : (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, [CallerArgumentExpression("value")] string name = "") + { + var difference = value >= target ? (nuint)(value - target) : (nuint)(target - value); + + if (difference > delta) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForIsNotCloseTo(value, target, delta, name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Guard.IO.cs b/Source/FisheryLib/Diagnostics/Guard.IO.cs new file mode 100644 index 0000000..e645849 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Guard.IO.cs @@ -0,0 +1,80 @@ +// 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 FisheryLib.Utility.Diagnostics; + +/// +public 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("stream")] string name = "") + { + if (stream.Position == 0) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForIsAtStartPosition(stream, name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Guard.String.cs b/Source/FisheryLib/Diagnostics/Guard.String.cs new file mode 100644 index 0000000..ebbab5f --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Guard.String.cs @@ -0,0 +1,334 @@ +// 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.Runtime.CompilerServices; + +#pragma warning disable CS8777 + +namespace FisheryLib.Utility.Diagnostics; + +/// +public 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("text")] string name = "") + { + if (text.Length != 0) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForIsNotEmpty(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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("text")] 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, [CallerArgumentExpression("text")] 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, [CallerArgumentExpression("text")] 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, [CallerArgumentExpression("text")] 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, [CallerArgumentExpression("text")] 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, [CallerArgumentExpression("text")] 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 text, string destination, [CallerArgumentExpression("text")] string name = "") + { + if (text.Length == destination.Length) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForHasSizeEqualTo(text, 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 text, string destination, [CallerArgumentExpression("text")] string name = "") + { + if (text.Length <= destination.Length) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForHasSizeLessThanOrEqualTo(text, 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, [CallerArgumentExpression("index")] 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, [CallerArgumentExpression("index")] string name = "") + { + if ((uint)index >= (uint)text.Length) + { + return; + } + + ThrowHelper.ThrowArgumentOutOfRangeExceptionForIsNotInRangeFor(index, text, name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Guard.Tasks.cs b/Source/FisheryLib/Diagnostics/Guard.Tasks.cs new file mode 100644 index 0000000..ee38611 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Guard.Tasks.cs @@ -0,0 +1,184 @@ +// 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; +using System.Threading.Tasks; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("task")] 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, [CallerArgumentExpression("task")] string name = "") + { + if (task.Status != status) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForHasStatusNotEqualTo(task, status, name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Guard.cs b/Source/FisheryLib/Diagnostics/Guard.cs new file mode 100644 index 0000000..a3aeff4 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Guard.cs @@ -0,0 +1,248 @@ +// 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; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace FisheryLib.Utility.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, [CallerArgumentExpression("value")] string name = "") + { + 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 . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IsNull(T? value, [CallerArgumentExpression("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, [CallerArgumentExpression("value")] string name = "") + { + 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 . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IsNotNull([NotNull] T? value, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("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, [CallerArgumentExpression("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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] 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, [CallerArgumentExpression("value")] string name = "") + where T : class + { + if (!ReferenceEquals(value, target)) + return; + + ThrowHelper.ThrowArgumentExceptionForIsReferenceNotEqualTo(name); + } +} diff --git a/Source/FisheryLib/Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs b/Source/FisheryLib/Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs new file mode 100644 index 0000000..4d13d6d --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Internals/Guard.Collection.Generic.ThrowHelper.cs @@ -0,0 +1,47 @@ +// 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; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public partial class Guard +{ + /// + private 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); + } + } +} diff --git a/Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs b/Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs new file mode 100644 index 0000000..2264eba --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Generic.ThrowHelper.cs @@ -0,0 +1,123 @@ +// 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; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public partial class Guard +{ + /// + private 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)}."); + } +} diff --git a/Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs b/Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs new file mode 100644 index 0000000..4810078 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Internals/Guard.Comparable.Numeric.ThrowHelper.cs @@ -0,0 +1,75 @@ +// 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; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public partial class Guard +{ + /// + private 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); + } +} diff --git a/Source/FisheryLib/Diagnostics/Internals/Guard.IO.ThrowHelper.cs b/Source/FisheryLib/Diagnostics/Internals/Guard.IO.ThrowHelper.cs new file mode 100644 index 0000000..9053433 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Internals/Guard.IO.ThrowHelper.cs @@ -0,0 +1,40 @@ +// 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.IO; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public partial class Guard +{ + /// + private 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); + } +} diff --git a/Source/FisheryLib/Diagnostics/Internals/Guard.String.ThrowHelper.cs b/Source/FisheryLib/Diagnostics/Internals/Guard.String.ThrowHelper.cs new file mode 100644 index 0000000..0ff9c6e --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Internals/Guard.String.ThrowHelper.cs @@ -0,0 +1,158 @@ +// 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.Runtime.CompilerServices; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public partial class Guard +{ + /// + private 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) + => text is null + ? new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or empty, was null.") + : new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or empty, was empty.", name); + + 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) + => text is null + ? new ArgumentNullException(name, $"Parameter {AssertString(name)} (string) must not be null or whitespace, was null.") + : new ArgumentException($"Parameter {AssertString(name)} (string) must not be null or whitespace, was whitespace.", name); + + 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 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)}."); + } +} diff --git a/Source/FisheryLib/Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs b/Source/FisheryLib/Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs new file mode 100644 index 0000000..4f46fab --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Internals/Guard.Tasks.ThrowHelper.cs @@ -0,0 +1,76 @@ +// 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.Threading.Tasks; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public partial class Guard +{ + /// + private 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); + } +} diff --git a/Source/FisheryLib/Diagnostics/Internals/Guard.ThrowHelper.cs b/Source/FisheryLib/Diagnostics/Internals/Guard.ThrowHelper.cs new file mode 100644 index 0000000..df45c9b --- /dev/null +++ b/Source/FisheryLib/Diagnostics/Internals/Guard.ThrowHelper.cs @@ -0,0 +1,144 @@ +// 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; + +namespace FisheryLib.Utility.Diagnostics; + +/// +public 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. + private static string AssertString(object? obj) + => 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) => 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); + } +} diff --git a/Source/FisheryLib/Diagnostics/ThrowHelper.Generic.cs b/Source/FisheryLib/Diagnostics/ThrowHelper.Generic.cs new file mode 100644 index 0000000..e04a2b7 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/ThrowHelper.Generic.cs @@ -0,0 +1,843 @@ +// 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.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +namespace FisheryLib.Utility.Diagnostics; + +/// +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); + + /// + /// 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 error 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 error 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); +} diff --git a/Source/FisheryLib/Diagnostics/ThrowHelper.cs b/Source/FisheryLib/Diagnostics/ThrowHelper.cs new file mode 100644 index 0000000..5a7f795 --- /dev/null +++ b/Source/FisheryLib/Diagnostics/ThrowHelper.cs @@ -0,0 +1,683 @@ +// 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.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +namespace FisheryLib.Utility.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); + + /// + /// Throws a new . + /// + /// Thrown with no parameters. + [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 error 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 error to include. + /// Thrown with the specified parameters. + [DoesNotReturn] + public static void ThrowExternalException(string? message, int error) => throw new ExternalException(message, error); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); + + /// + /// 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); +} diff --git a/Source/FisheryLib/Equality.cs b/Source/FisheryLib/Equality.cs new file mode 100644 index 0000000..fe90726 --- /dev/null +++ b/Source/FisheryLib/Equality.cs @@ -0,0 +1,29 @@ +// /* Copyright (c) 2022 bradson +// * This Source Code Form is subject to the terms of the Mozilla Public +// * License, v. 2.0. If a copy of the MPL was not distributed with this +// * file, You can obtain one at https://mozilla.org/MPL/2.0/. +// */ + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace FisheryLib; + +public static class Equality +{ + /// + /// Generic overload of Equals to avoid boxing. Makes use of function pointers for best performance and does not throw on null for reference types. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool Equals([AllowNull] this T @this, [AllowNull] T other) + => FunctionPointers.Equals.Default(@this!, other!); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool Equals([AllowNull] this ref T @this, [AllowNull] ref T other) + where T : struct // required by the compiler for 'this ref' + => FunctionPointers.EqualsByRef.Default(ref @this!, ref other!); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool EqualsByRef([AllowNull] ref T @this, [AllowNull] ref T other) + => FunctionPointers.EqualsByRef.Default(ref @this!, ref other!); +} \ No newline at end of file diff --git a/Source/FisheryLib/FishPatch.cs b/Source/FisheryLib/FishPatch.cs new file mode 100644 index 0000000..c33ba8b --- /dev/null +++ b/Source/FisheryLib/FishPatch.cs @@ -0,0 +1,338 @@ +// Copyright (c) 2022 bradson +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +/*using System.Collections; +using System.Linq; +using System.Linq.Expressions; +using Verse; + +namespace FisheryLib; + +internal class FishPatchManager +{ + internal const string FISHERY = "fishery"; + + internal FishPatchManager(Assembly assembly) + { + Assembly = assembly; + ModContentPack = LoadedModManager.RunningModsListForReading.Find(pack => pack.assemblies.loadedAssemblies.Contains(assembly)); + ModName = ModContentPack?.Name ?? assembly.GetName().Name; + ModPackageID = ModContentPack?.PackageIdPlayerFacing; + Harmony = new($"{FISHERY}.{ModPackageID ?? ModName.ToLowerInvariant()}"); + + } + + public Assembly Assembly { get; } + public ModContentPack? ModContentPack { get; } + public string? ModPackageID { get; } + public string ModName { get; } + public Harmony Harmony { get; } +} + +public abstract class FishPatch : IExposable, IHasDescription +{ + public string ModName = "Fishery"; + public Harmony Harmony { get; } = new("bs.fishery"); + public void PatchAll() + { + foreach (var patch in AllPatchClasses) + { + try + { + if (!patch.RequiresLoadedGameForPatching) + patch.Patches.PatchAll(); + } + catch (Exception ex) + { + Log.Error($"{ModName} encountered an exception while patching:\n{ex}"); + } + } + } + public bool Enabled + { + get => _enabled; + set + { + _enabled = value; + if (value) + { + if (ShouldBePatched) + TryPatch(); + } + else + { + var shouldBePatched = ShouldBePatched; + TryUnpatch(); + ShouldBePatched = shouldBePatched; + } + } + } + private bool _enabled = true; + public virtual bool DefaultState => true; + public virtual int PrefixMethodPriority => TryGetPriority(PrefixMethodInfo); + public virtual int PostfixMethodPriority => TryGetPriority(PostfixMethodInfo); + public virtual int TranspilerMethodPriority => TryGetPriority(TranspilerMethodInfo); + public virtual int FinalizerMethodPriority => TryGetPriority(FinalizerMethodInfo); + public MethodInfo? HarmonyMethodInfo { get; private set; } + private bool Patched { get; set; } + private bool ShouldBePatched { get; set; } + + public virtual string Name => GetType().Name; + public virtual string? Description => null; + public virtual bool ShowSettings => true; + + public virtual Delegate? TargetMethodGroup => null; + public virtual IEnumerable? TargetMethodGroups => null; + public virtual Expression? TargetMethod => null; + public virtual IEnumerable>? TargetMethods => null; + public virtual MethodBase TargetMethodInfo => TargetMethod != null ? SymbolExtensions.GetMethodInfo(TargetMethod) + : TargetMethodGroup?.Method ?? throw new MissingMethodException(GetType().ToString()); + public virtual IEnumerable TargetMethodInfos => TargetMethods?.Select(m => SymbolExtensions.GetMethodInfo(m)) + ?? TargetMethodGroups?.Select(m => m.Method) ?? (TargetMethodInfo != null ? new MethodBase[] { TargetMethodInfo }.AsEnumerable() : throw new MissingMethodException(GetType().ToString())); + public virtual MethodInfo ReversePatchMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "REVERSEPATCH" || (m.HasAttribute() && !m.HasAttribute())); + public virtual MethodInfo ReversePatchTranspilerMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "REVERSEPATCHTRANSPILER" || (m.HasAttribute() && m.HasAttribute())); + public virtual MethodInfo PrefixMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "PREFIX" || m.HasAttribute()); + public virtual MethodInfo PostfixMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "POSTFIX" || m.HasAttribute()); + public virtual MethodInfo TranspilerMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "TRANSPILER" || (m.HasAttribute() && !m.HasAttribute())); + public virtual MethodInfo FinalizerMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "FINALIZER" || m.HasAttribute()); + public virtual MethodInfo PrepareMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "PREPARE" || m.HasAttribute()); + public virtual MethodInfo CleanupMethodInfo => GetType().GetMethods(AccessTools.allDeclared) + .FirstOrDefault(m => m.Name.ToUpperInvariant() == "CLEANUP" || m.HasAttribute()); + + public FishPatch() => _enabled = DefaultState; + + public virtual MethodInfo? TryPatch() + { + if (TargetMethodInfos is null || !TargetMethodInfos.Any()) + { + Log.Error($"Tried to apply patches for {GetType().Name}, but it lacks a target method for patching."); + return null; + } + if (PrefixMethodInfo is null && PostfixMethodInfo is null && TranspilerMethodInfo is null && FinalizerMethodInfo is null && ReversePatchMethodInfo is null) + { + Log.Error($"Tried to apply patches for {GetType().Name}, but there are none. This is likely not intended."); + return null; + } + + ShouldBePatched = true; + if (Enabled && !Patched) + { + Patched = true; + + PrepareMethodInfo?.Invoke(null, null); + foreach (var method in TargetMethodInfos) + { + try + { + if (ReversePatchMethodInfo != null) + HarmonyMethodInfo = Harmony.ReversePatch(method, new(ReversePatchMethodInfo), ReversePatchTranspilerMethodInfo); + + if (PrefixMethodInfo != null || PostfixMethodInfo != null || TranspilerMethodInfo != null || FinalizerMethodInfo != null) + { + HarmonyMethodInfo = Harmony.Patch(method, + prefix: PrefixMethodInfo != null ? new(PrefixMethodInfo, PrefixMethodPriority) : null, + postfix: PostfixMethodInfo != null ? new(PostfixMethodInfo, PostfixMethodPriority) : null, + transpiler: TranspilerMethodInfo != null ? new(TranspilerMethodInfo, TranspilerMethodPriority) : null, + finalizer: FinalizerMethodInfo != null ? new(FinalizerMethodInfo, FinalizerMethodPriority) : null); + } + } + catch (Exception e) + { + Log.Error($"{ModName} encountered an exception while trying to patch {method.FullDescription()} with {PrefixMethodInfo.FullDescription()}" + + $", {PostfixMethodInfo.FullDescription()}, {TranspilerMethodInfo.FullDescription()}, {FinalizerMethodInfo.FullDescription()}:\n{e}"); + } + +#if DEBUG + Log.Message($"{ModName} applied {GetType().Name} on {method.FullDescription()}"); +#endif + } + } + return HarmonyMethodInfo; + } + + public virtual void TryUnpatch() + { + ShouldBePatched = false; + if (Patched) + { + Patched = false; + foreach (var method in TargetMethodInfos) + { + if (PrefixMethodInfo != null) + { + try + { + Harmony.Unpatch(method, PrefixMethodInfo); + } + catch (Exception e) + { + Log.Error($"{ModName} encountered an exception when unpatching {PrefixMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); + } + } + if (PostfixMethodInfo != null) + { + try + { + Harmony.Unpatch(method, PostfixMethodInfo); + } + catch (Exception e) + { + Log.Error($"{ModName} encountered an exception when unpatching {PostfixMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); + } + } + if (TranspilerMethodInfo != null) + { + try + { + Harmony.Unpatch(method, TranspilerMethodInfo); + } + catch (Exception e) + { + Log.Error($"{ModName} encountered an exception when unpatching {TranspilerMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); + } + } + if (FinalizerMethodInfo != null) + { + try + { + Harmony.Unpatch(method, FinalizerMethodInfo); + } + catch (Exception e) + { + Log.Error($"{ModName} encountered an exception when unpatching {FinalizerMethodInfo.FullDescription()} from {method.FullDescription()}:\n{e}"); + } + } + } + CleanupMethodInfo?.Invoke(null, null); + } + } + + private static int TryGetPriority(MethodInfo info) => info?.TryGetAttribute()?.info.priority ?? Priority.Normal; + + public static T Get() => TryGet() ?? throw new KeyNotFoundException($"Couldn't find FishPatch or IHasFishPatch class {typeof(T).Name}"); + public static T? TryGet() + { + if (typeof(IHasFishPatch).IsAssignableFrom(typeof(T))) + { + foreach (var patch in AllPatchClasses) + { + if (patch is T patchOfT) + return patchOfT; + } + } + else if (typeof(FishPatch).IsAssignableFrom(typeof(T))) + { + foreach (var patchClass in AllPatchClasses) + { + if (patchClass.Patches.TryGet(typeof(T)) is T patch) + return patch; + } + } + + return default; + } + public static IHasFishPatch[] AllPatchClasses { get; } = Assembly.GetExecutingAssembly().GetTypes() + .Where(t => typeof(IHasFishPatch).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) + .Select(t => (IHasFishPatch)Activator.CreateInstance(t)).ToArray(); + public void ExposeData() => Scribe_Values.Look(ref _enabled, GetType().Name, DefaultState); +} + +public abstract class FirstPriorityFishPatch : FishPatch +{ + public override int PrefixMethodPriority => Priority.First; + public override int PostfixMethodPriority => Priority.First; + public override int TranspilerMethodPriority => Priority.First; + public override int FinalizerMethodPriority => Priority.First; +} + +public static class FishPatchExtensions +{ + public static void PatchAll(this IEnumerable patches) + { + foreach (var patch in patches) + patch.TryPatch(); + } + public static void UnpatchAll(this IEnumerable patches) + { + foreach (var patch in patches) + patch.TryUnpatch(); + } +} + +public class FishPatchHolder : IExposable, IEnumerable +{ + public Dictionary All => _all; + public T Get() where T : FishPatch => (T)All[typeof(T)]; + public FishPatch Get(Type type) => All[type]; + public T? TryGet() where T : FishPatch => All!.TryGetValue(typeof(T)) as T; + public FishPatch? TryGet(Type type) => All!.TryGetValue(type); + public void Add(FishPatch patch) => All[patch.GetType()] = patch; + public void PatchAll() + { + AddPatchesRecursively(_type); + All.Values.PatchAll(); + } + public void UnpatchAll() => All.Values.UnpatchAll(); + public FishPatch this[Type type] => All[type]; + + public FishPatchHolder(Type type) => _type = type; + + public void ExposeData() => Scribe_Collections.Look(ref _all, "patches", valueLookMode: LookMode.Deep); + + private void AddPatchesRecursively(Type type) + { + if (typeof(FishPatch).IsAssignableFrom(type) && !All.ContainsKey(type)) + { + var dupeClasses = FishPatch.AllPatchClasses.Where(patchClass => patchClass.GetType() != _type && patchClass.Patches.All.ContainsKey(type)); + if (dupeClasses.Any()) + { + foreach (var patchClass in dupeClasses) + { + patchClass.Patches[type].Enabled = false; + patchClass.Patches.All.Remove(type); + Log.Warning($"{FishPatchManager.FISHERY} removed a duplicate patch from {patchClass.GetType().FullName}. This is likely caused by no longer valid mod configs"); + } + } + + All.Add(type, (FishPatch)Activator.CreateInstance(type)); + } + + foreach (var nestedType in type.GetNestedTypes(AccessTools.all)) + AddPatchesRecursively(nestedType); + } + + public IEnumerator GetEnumerator() => All.Values.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => All.Values.GetEnumerator(); + + private Dictionary _all = new(); + private Type _type; +} + +public interface IHasFishPatch +{ + public FishPatchHolder Patches { get; } + public FishPatchHolder? PatchHolder { get; set; } + public bool RequiresLoadedGameForPatching { get; } +} + +public interface IHasDescription +{ + public string? Description { get; } +} + +public abstract class ClassWithFishPatches : IHasFishPatch +{ + public virtual FishPatchHolder Patches => _patchHolder ??= new(GetType()); + public virtual FishPatchHolder? PatchHolder { get => _patchHolder; set => _patchHolder = value; } + public virtual bool RequiresLoadedGameForPatching => false; + private FishPatchHolder? _patchHolder; +}*/ \ No newline at end of file diff --git a/Source/FisheryLib/FishTranspiler.cs b/Source/FisheryLib/FishTranspiler.cs new file mode 100644 index 0000000..9c3e999 --- /dev/null +++ b/Source/FisheryLib/FishTranspiler.cs @@ -0,0 +1,2403 @@ +// Copyright (c) 2022 bradson +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection.Emit; + +namespace FisheryLib; +public static class FishTranspiler +{ + public struct Container + { + public Container(OpCode opcode, object? operand = null) + { + OpCode = opcode; + Operand = operand; + } + + public Container(CodeInstruction instruction) + { + Guard.IsNotNull(instruction, nameof(instruction)); + + OpCode = instruction.opcode; + Operand = instruction.operand; + } + + public Container(Container container) + { + OpCode = container.OpCode; + Operand = container.Operand; + } + + public override string ToString() + => $"{OpCode} {Operand}"; + + public CodeInstruction ToInstruction() + => new(OpCode, Operand); + + public Container Store() + => OpCode.LoadsArgument() ? ToStoreArgument() + : new( + OpCode.LoadsField() || OpCode.StoresField() ? OpCode.ToStoreField() + : OpCode.LoadsLocalVariable() || OpCode.StoresLocalVariable() ? OpCode.ToStoreLocalVariable() + : OpCode.StoresArgument() ? OpCode + : ThrowHelper.ThrowInvalidOperationException($"Cannot determine Store instruction for {OpCode}"), + Operand); + + public Container Load() + => OpCode.StoresArgument() ? ToLoadArgument() + : new( + OpCode.LoadsField() || OpCode.StoresField() ? OpCode.ToLoadField() + : OpCode.LoadsLocalVariable() || OpCode.StoresLocalVariable() ? OpCode.ToLoadLocalVariable() + : OpCode.LoadsArgument() ? OpCode + : ThrowHelper.ThrowInvalidOperationException($"Cannot determine Load instruction for {OpCode}"), + Operand); + + private Container ToStoreArgument() + => new(OpCode.ToStoreArgument(), GetIndex()); + + private Container ToLoadArgument() + { + var index = GetIndex(); + return new(GetLoadArgumentOpCode(index), index > 3 ? index : null); + } + + public Container Address() + => new(OpCode.ToAddress(), GetIndex()); + + public CodeInstruction WithLabels(params Label[] labels) + => WithLabels((IEnumerable